6783fdb0b862beb0b85ba1ec7d73d91027ef850e
[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        "md4.h"
60 #include        "md5.h"
61 #include        "sha1.h"
62 #include        "smbpass.h"
63 #include        "rad_assert.h"
64
65 #define PW_MSCHAP_RESPONSE      ((311 << 16) | 1)
66 #define PW_MSCHAP_CHALLENGE     ((311 << 16) | 11)
67 #define PW_MSCHAP2_RESPONSE     ((311 << 16) | 25)
68
69
70 typedef enum {
71         NONE,
72         CLEARTEXT,
73         MSCHAP1,
74         MSCHAP2} AUTHTYPE;
75
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,
84                 char *response);
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);
91
92 static void mppe_add_reply(VALUE_PAIR** vp,
93                const char* name, const char* value, int len);
94
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);
98
99 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
100                                uint8_t *sendkey,uint8_t *recvkey);
101
102 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
103                        uint8_t *masterkey);
104
105 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
106                                int keylen,int issend);
107
108 #if 0
109 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
110                        uint8_t* salt,uint8_t* enckey,uint8_t* key);
111 #endif
112
113
114
115 /* 
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.
119  */
120 static void parity_key(char * szOut, const char * szIn)
121 {
122         int i;
123         unsigned char cNext = 0;
124         unsigned char cWorking = 0;
125         
126         for (i = 0; i < 7; i++) {
127                 /* Shift operator works in place.  Copy the char out */
128                 cWorking = szIn[i];
129                 szOut[i] = (cWorking >> i) | cNext | 1;
130                 cWorking = szIn[i];
131                 cNext = (cWorking << (7 - i));
132         }
133         szOut[i] = cNext | 1;
134 }
135
136 /*
137  *      des_encrypt takes an 8-byte string and a 7-byte key and
138  *      returns an 8-byte DES encrypted string in szOut
139  */
140 static void des_encrypt(const char *szClear, const char *szKey, char *szOut)
141 {
142         char szParityKey[9];
143         unsigned long ulK[16][2];
144         
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 */
149 }
150
151
152
153 /*
154  *      ntpwdhash converts Unicode password to 16-byte NT hash
155  *      with MD4
156  */
157 static void ntpwdhash (char *szHash, const char *szPassword)
158 {
159         char szUnicodePass[513];
160         char nPasswordLen;
161         int i;
162
163         /*
164          *      NT passwords are unicode.  Convert plain text password
165          *      to unicode by inserting a zero every other byte
166          */
167         nPasswordLen = strlen(szPassword);
168         for (i = 0; i < nPasswordLen; i++) {
169                 szUnicodePass[i << 1] = szPassword[i];
170                 szUnicodePass[(i << 1) + 1] = 0;
171         }
172
173         /* Encrypt Unicode password to a 16-byte MD4 hash */
174         md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
175 }
176
177
178
179 /*
180  *      lmpwdhash converts 14-byte null-padded uppercase OEM
181  *      password to 16-byte DES hash with predefined salt string
182  */
183 static void lmpwdhash (char *szHash, const char *szPassword)
184 {
185         char szOEMPass[14];
186         char stdText[] = "KGS!@#$%";
187         int i;
188
189         memset(szOEMPass, 0, 14);
190         for (i = 0; i < 14 && szPassword[i]; i++)
191                 szOEMPass[i] = toupper((int) szPassword[i]);
192
193         /* Obtain DES hash of OEM password */
194         des_encrypt(stdText, szOEMPass, szHash); 
195         des_encrypt(stdText, szOEMPass+7, szHash+8);
196 }
197
198 /*
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
202  *      allowed
203  */
204 static struct smb_passwd *createsmbpw(struct smb_passwd *pw_buf, const char * username, const char *password)
205 {
206         if(pw_buf == NULL) {
207                 return NULL;
208         }
209         pdb_init_smb(pw_buf);
210         pw_buf->acct_ctrl = ACB_NORMAL;
211         pw_buf->smb_userid = 0;
212         setsmbname(pw_buf,username);
213   
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;
219         }
220         return pw_buf;
221 }
222
223
224
225 /*
226  *      mschap takes an 8-byte challenge string and SMB password
227  *      and returns a 24-byte response string in szResponse
228  */
229 static void mschap(const char *szChallenge, struct smb_passwd * smbPasswd,
230         char *szResponse, int bUseNT) {
231
232         char szMD4[21];
233         
234         /* initialize hash string */
235         memset(szMD4, 0, 21);
236         
237         memcpy(szMD4, (bUseNT)?
238                 smbPasswd->smb_nt_passwd : smbPasswd->smb_passwd, 16);
239         
240         /*
241          *
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
245          */
246         des_encrypt(szChallenge, szMD4, szResponse);
247         des_encrypt(szChallenge, szMD4 + 7, szResponse + 8);
248         des_encrypt(szChallenge, szMD4 + 14, szResponse + 16);
249 }   
250
251
252 /*
253  *      challenge_hash() is used by mschap2() and auth_response()
254  *      implements RFC2759 ChallengeHash()
255  *      generates 64 bit challenge
256  */
257 static void challenge_hash( const char* peer_challenge, const char* auth_challenge,
258                      const char* user_name, char * challenge )
259 {
260         SHA1_CTX Context;
261         char hash[20];
262         
263         SHA1Init(&Context);
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);
269 }
270
271 static void mschap2( const char *peer_challenge, const char *auth_challenge,
272                 struct smb_passwd * smbPasswd, char *response)
273 {
274         char challenge[8];
275         
276         challenge_hash(peer_challenge, auth_challenge, smbPasswd->smb_name,
277                 challenge);
278         mschap(challenge, smbPasswd, response, 1);
279 }
280
281 /*
282  *      auth_response() generates MS-CHAP v2 SUCCESS response
283  *      according to RFC 2759 GenerateAuthenticatorResponse()
284  *      returns 42-octet response string
285  */
286 static void auth_response(struct smb_passwd * smbPasswd, char *ntresponse,
287                 char *peer_challenge, char *auth_challenge,
288                 char *response)
289 {
290         SHA1_CTX Context;
291         char hashhash[16];
292         char magic1[39] =
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};
297                                              
298         char magic2[41] =
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,
303                 0x6E};
304         char challenge[8];
305         char digest[20];
306
307         /*
308          * Hash password hash into hashhash
309          */
310
311         md4_calc(hashhash, smbPasswd->smb_nt_passwd, 16);
312
313         SHA1Init(&Context);
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,
319                 challenge);
320         SHA1Init(&Context);
321         SHA1Update(&Context, digest, 20);
322         SHA1Update(&Context, challenge, 8);
323         SHA1Update(&Context, magic2, 41);
324         SHA1Final(digest, &Context);
325
326         /*
327          * Encode the value of 'Digest' as "S=" followed by
328          * 40 ASCII hexadecimal digits and return it in
329          * AuthenticatorResponse.
330          * For example,
331          *   "S=0123456789ABCDEF0123456789ABCDEF01234567"
332          */
333  
334         response[0] = 'S';
335         response[1] = '=';
336         bin2hex(digest, response + 2, 20);
337 }
338
339 struct mschap_instance {
340         int ignore_password;
341         int use_mppe;
342         int require_encryption;
343         int require_strong;
344         char *passwd_file;
345         char *auth_type;
346 };
347
348 static CONF_PARSER module_config[] = {
349         /*
350          *      Cache the password by default.
351          */
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 },
364         
365         { NULL, -1, 0, NULL, NULL }             /* end the list */
366 };
367
368 /*
369  *      Create instance for our module. Allocate space for
370  *      instance structure and read configuration parameters
371  */
372 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
373 {
374         struct mschap_instance *inst;
375
376         inst = *instance = rad_malloc(sizeof(struct mschap_instance));
377         if (cf_section_parse(conf, inst, module_config) < 0) {
378                 free(inst);
379                 return -1;
380         }
381         return 0;
382 }
383
384 /*
385  *      deinstantiate module, free all memory allocated during
386  *      mschap_instantiate()
387  */
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);
392         free(instance);
393         return 0;
394 #undef inst
395 }
396         
397 /*
398  *      add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
399  *      attribute to reply packet
400  */
401 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
402                 const char* name, const char* value, int len)
403 {
404         VALUE_PAIR *reply_attr;
405         reply_attr = pairmake(name, "", T_OP_EQ);
406         if (!reply_attr) {
407                 DEBUG("rlm_mschap: add_reply failed to create attribute %s: %s\n", name, librad_errstr);
408                 return;
409         }
410
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);
415 }
416
417 static void mppe_add_reply(VALUE_PAIR** vp,
418                        const char* name, const char* value, int len)
419 {
420        VALUE_PAIR *reply_attr;
421        reply_attr = pairmake(name, "", T_OP_EQ);
422        if (!reply_attr) {
423                DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
424                return;
425        }
426
427        memcpy(reply_attr->strvalue, value, len);
428        reply_attr->length = len;
429        pairadd(vp, reply_attr);
430 }
431
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)
435 {
436         uint8_t enckey1[16];
437         uint8_t enckey2[16];
438         /* uint8_t salt[2]; */
439         uint8_t nt_hashhash[16];
440
441         md4_calc(nt_hashhash,nt_hash,16);
442
443         mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
444 #if 0
445         salt[0] = (vector[0] ^ vector[1] ^ 0x3A) | 0x80;
446         salt[1] = (vector[2] ^ vector[3] ^ vector[4]);
447
448         mppe_gen_respkey(secret,vector,salt,enckey1,sendkey);
449
450         salt[0] = (vector[0] ^ vector[1] ^ 0x4e) | 0x80;
451         salt[1] = (vector[5] ^ vector[6] ^ vector[7]);
452
453         mppe_gen_respkey(secret,vector,salt,enckey2,recvkey);
454 #endif
455         memcpy (sendkey, enckey1, 16);
456         memcpy (recvkey, enckey2, 16);
457 }
458
459 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
460                                uint8_t *sendkey,uint8_t *recvkey)
461 {
462        uint8_t masterkey[16];
463
464        mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
465
466        mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
467        mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
468 }
469
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 };
475
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 };
481
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 };
486
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 };
497
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 };
508
509
510 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
511                        uint8_t *masterkey)
512 {
513        uint8_t digest[20];
514        SHA1_CTX Context;
515
516        SHA1Init(&Context);
517        SHA1Update(&Context,nt_hashhash,16);
518        SHA1Update(&Context,nt_response,24);
519        SHA1Update(&Context,magic1,27);
520        SHA1Final(digest,&Context);
521
522        memcpy(masterkey,digest,16);
523 }
524
525
526 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
527                                int keylen,int issend)
528 {
529        uint8_t digest[20];
530        uint8_t *s;
531        SHA1_CTX Context;
532
533        memset(digest,0,20);
534
535        if(issend) {
536                s = magic3;
537        } else {
538                s = magic2;
539        }
540
541        SHA1Init(&Context);
542        SHA1Update(&Context,masterkey,16);
543        SHA1Update(&Context,SHSpad1,40);
544        SHA1Update(&Context,s,84);
545        SHA1Update(&Context,SHSpad2,40);
546        SHA1Final(digest,&Context);
547
548        memcpy(sesskey,digest,keylen);
549 }
550
551
552 #if 0
553 /*      Not requiered, because encoding will be performed by
554         tunnel_pwencode */
555 static void mppe_gen_respkey(uint8_t* secret,uint8_t* vector,
556                        uint8_t* salt,uint8_t* enckey,uint8_t* key)
557 {
558        uint8_t plain[32];
559        uint8_t buf[16];
560        int i;
561        MD5_CTX Context;
562        int slen = strlen(secret);
563
564        memset(key,0,34);
565
566        memset(plain,0,32);
567        plain[0] = 16;
568        memcpy(plain + 1,enckey,16);
569
570        MD5Init(&Context);
571        MD5Update(&Context,secret,slen);
572        MD5Update(&Context,vector,AUTH_VECTOR_LEN);
573        MD5Update(&Context,salt,2);
574        MD5Final(buf,&Context);
575
576        for(i=0;i < 16;i++) {
577                plain[i] ^= buf[i];
578        }
579
580        MD5Init(&Context);
581        MD5Update(&Context,secret,slen);
582        MD5Update(&Context,plain,16);
583        MD5Final(buf,&Context);
584
585        for(i=0;i < 16;i++) {
586                plain[i + 16] ^= buf[i];
587        }
588
589        memcpy(key,salt,2);
590        memcpy(key + 2,plain,32);
591 }
592 #endif /* 0 */
593
594
595 /*
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")
599  */
600 static int mschap_authorize(void * instance, REQUEST *request)
601 {
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;
607
608         
609         password = pairfind(request->config_items, PW_PASSWORD);
610         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
611         if (challenge) {
612                 response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
613                 if (!response)
614                         response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
615         }
616         if (password && (!challenge || !response)) {
617                 /*  We have nothing related to MS-CHAP or NTLM */
618                 return RLM_MODULE_NOOP;
619         }
620         if (!request->username || *request->username->strvalue == 0) {
621                 /* Usernam must present */
622                 return RLM_MODULE_NOOP;
623         }
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);
628         }
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);
633                 }
634                 return RLM_MODULE_NOTFOUND;
635         }
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);
641         }
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);
648         }
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);
655         }
656
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;
662 #undef inst
663 }
664
665
666 /*
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.
672  *
673  *      If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have 
674  *      one of:
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
681  *      PW_MSCHAP2_SUCCESS
682  */
683 static int mschap_authenticate(void * instance, REQUEST *request)
684 {
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;
694         AUTHTYPE at = NONE;
695         int res = 0;
696 /*
697         int len = 0;
698 */
699         int chap = 0;
700         
701         pdb_init_smb(&smbPasswd);
702         setsmbname(&smbPasswd,request->username->strvalue);
703         password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
704         if (password) {
705                 smbPasswd.acct_ctrl = password->lvalue;
706                 if (smbPasswd.acct_ctrl & ACB_PWNOTREQ) return RLM_MODULE_OK;
707         }
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
715         password = pairfind(request->config_items, PW_LM_PASSWORD);
716         if (password) {
717                 if(password->length == 16) {
718                         smbPasswd.smb_passwd = password->strvalue;
719                         res++;
720                 }
721                 else if(hex2bin(password->strvalue,smbPasswd.smb_passwd_value,16) != 16) {
722                         radlog(L_ERR, "rlm_mschap: Invalid LM Password text");
723                 } 
724                 else {
725                         res++;
726                         smbPasswd.smb_passwd = smbPasswd.smb_passwd_value;
727                 }
728                 
729         }
730
731         password = pairfind(request->config_items, PW_NT_PASSWORD);
732         if (password) {
733                 if (password->length == 16) {
734                         smbPasswd.smb_nt_passwd = password->strvalue;
735                         res++;
736                 }
737                 else if(hex2bin(password->strvalue,smbPasswd.smb_nt_passwd_value,16) != 16) {
738                         radlog(L_ERR, "rlm_mschap: Invalid NT Password text");
739                 }
740                 else {
741                         smbPasswd.smb_nt_passwd = smbPasswd.smb_nt_passwd_value;
742                         res++;
743                 }
744         }
745         if (!res) {
746         /*
747          * We have neither NT nor LM passwords configured
748          */
749                 radlog(L_ERR, "rlm_mschap: No LM/NT password configured. Check authorization.");
750                 return RLM_MODULE_INVALID;
751          }
752         
753         /*
754          *      If NAS sent cleartext password - encode it and check
755          *      only against passwd file. If either NT or LM hash match
756          *      return OK.
757          */
758
759         password = pairfind(request->packet->vps, PW_PASSWORD);
760         if (password && request->username && 
761             (request->username->strvalue[0] != '\0')) {
762                 at = CLEARTEXT;
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;
767                         }
768                         DEBUG2("rlm_mschap: Found SMB password, but user supplied password is incorrect");
769
770                 } else if (smbPasswd.smb_nt_passwd) {
771                         if (memcmp(smbPasswd1->smb_nt_passwd, smbPasswd.smb_nt_passwd, 16)) {
772                                 return RLM_MODULE_OK;
773                         }
774                         DEBUG2("rlm_mschap: Found NT Password, but user supplied password is incorrect");
775                 } else {
776                         DEBUG2("rlm_mschap: No SMB or NT Password was configured for the user: Rejecting them.");
777                 }
778                         
779                 return RLM_MODULE_REJECT;
780         }
781
782         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
783         if (challenge) {
784                 /*
785                  *      We need an MS-CHAP-Challenge attribute to calculate
786                  *      the response.
787                  */
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;
793                         }
794                         /*
795                          *      We are doing MS-CHAP
796                          *      Calculate the MS-CHAP response
797                          */
798                         if (smbPasswd.smb_nt_passwd && (response->strvalue[1] & 0x01)) {
799                         /*
800                          * Try NT response first if UseNT flag is set
801                          */
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) {
805                                         res = RLM_MODULE_OK;
806                                 }
807                          }
808
809                         if (res != RLM_MODULE_OK && smbPasswd.smb_passwd) {
810                         /*
811                          *      Use LM response.
812                          */
813                                 DEBUG2("rlm_mschap: doing MS-CHAPv1 with LM-Password");
814                                 mschap(challenge->strvalue, &smbPasswd, 
815                                         calculated, 0);
816                                 if (memcmp(response->strvalue + 2, calculated, 24) == 0) {
817                                         res = RLM_MODULE_OK;
818                                 }
819                         }
820                         chap = 1;
821                 }
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;
826                         }
827                         /*
828                          *      We are doing MS-CHAPv2
829                          *      We need NT hash for it to calculate response
830                          */
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,
838                                                 challenge->strvalue,
839                                                 msch2resp);
840                                         add_reply( &request->reply->vps, *response->strvalue,
841                                                 "MS-CHAP2-Success", msch2resp, 42);
842                                         res = RLM_MODULE_OK;
843                                         chap = 2;
844                                 }
845                         }
846
847                 } else {
848                         radlog(L_AUTH, "rlm_mschap: Response attribute is not found");
849                         return RLM_MODULE_INVALID;
850                 }
851
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;
858                         }
859
860                         /* now create MPPE attributes */
861                         if (inst->use_mppe) {
862                                 if (chap == 1){
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)
868                                         /* 
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.
874
875                                                 memcpy (mppe_sendkey+8,smbPasswd.smb_nt_passwd,16);   
876                                         */
877                                                 md4_calc(mppe_sendkey+8, smbPasswd.smb_nt_passwd,16);
878 /*
879                                         rad_pwencode(mppe_sendkey, &len, 
880                                                  request->secret, request->packet->vector);
881 */
882                                         mppe_add_reply( &request->reply->vps,
883                                                 "MS-CHAP-MPPE-Keys",mppe_sendkey,32);
884                                 }
885                                 else if (chap == 2){
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);
891
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);
896
897 /*
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);
902 */
903                                 }
904                                 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
905                                         (inst->require_encryption)? "0x00000002":"0x00000001",
906                                         T_OP_EQ);
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",
911                                         T_OP_EQ);
912                                 rad_assert(reply_attr != NULL);
913                                 pairadd(&request->reply->vps, reply_attr);
914                         
915                         }
916                         return res;
917                 }
918                 else {
919                         DEBUG2("rlm_mschap: Authentication failed");
920                 }
921         }
922         
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");
926
927         return RLM_MODULE_REJECT;
928 #undef inst
929 }
930
931 module_t rlm_mschap = {
932   "MS-CHAP",
933   RLM_TYPE_THREAD_SAFE,                         /* type */
934   NULL,                         /* initialize */
935   mschap_instantiate,           /* instantiation */
936   {
937           mschap_authenticate,  /* authenticate */
938           mschap_authorize,     /* authorize */
939           NULL,                 /* pre-accounting */
940           NULL,                 /* accounting */
941           NULL                  /* checksimul */
942   },
943   mschap_detach,                /* detach */
944   NULL,                         /* destroy */
945 };