Whoops... that's a typo
[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  *  This implements MS-CHAP, as described in RFC 2548
28  *
29  *  http://www.freeradius.org/rfc/rfc2548.txt
30  *
31  */
32
33 /*
34  *  If you have any questions on NTLM (Samba) passwords
35  *  support, LM authentication and MS-CHAP v2 support
36  *  please contact
37  *
38  *  Vladimir Dubrovin   vlad@sandy.ru
39  *  aka
40  *  ZARAZA              3APA3A@security.nnov.ru
41  */
42  
43 /*  MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */
44
45 #include        "autoconf.h"
46 #include        "libradius.h"
47
48 #include        <stdio.h>
49 #include        <stdlib.h>
50 #include        <string.h>
51 #include        <ctype.h>
52
53 #include        "radiusd.h"
54 #include        "modules.h"
55
56 #include        "md4.h"
57 #include        "md5.h"
58 #include        "sha1.h"
59 #include        "rad_assert.h"
60
61 static const char rcsid[] = "$Id$";
62
63 static const char *letters = "0123456789ABCDEF";
64
65 /*
66  *      hex2bin converts hexadecimal strings into binary
67  */
68 static int hex2bin (const char *szHex, unsigned char* szBin, int len)
69 {
70         char * c1, * c2;
71         int i;
72    
73         for (i = 0; i < len; i++) {
74                 if( !(c1 = memchr(letters, toupper((int) szHex[i << 1]), 16)) ||
75                     !(c2 = memchr(letters, toupper((int) szHex[(i << 1) + 1]), 16)))
76                      break;
77                  szBin[i] = ((c1-letters)<<4) + (c2-letters);
78         }
79         return i;
80 }
81
82 /*
83  *      bin2hex creates hexadecimal presentation
84  *      of binary data
85  */ 
86 static void bin2hex (const unsigned char *szBin, char *szHex, int len)
87 {
88         int i;
89         for (i = 0; i < len; i++) {
90                 szHex[i<<1] = letters[szBin[i] >> 4];
91                 szHex[(i<<1) + 1] = letters[szBin[i] & 0x0F];
92         }
93 }
94
95
96 /* Allowable account control bits */
97 #define ACB_DISABLED   0x0001  /* 1 = User account disabled */
98 #define ACB_HOMDIRREQ  0x0002  /* 1 = Home directory required */
99 #define ACB_PWNOTREQ   0x0004  /* 1 = User password not required */
100 #define ACB_TEMPDUP    0x0008  /* 1 = Temporary duplicate account */
101 #define ACB_NORMAL     0x0010  /* 1 = Normal user account */
102 #define ACB_MNS        0x0020  /* 1 = MNS logon user account */
103 #define ACB_DOMTRUST   0x0040  /* 1 = Interdomain trust account */
104 #define ACB_WSTRUST    0x0080  /* 1 = Workstation trust account */
105 #define ACB_SVRTRUST   0x0100  /* 1 = Server trust account */
106 #define ACB_PWNOEXP    0x0200  /* 1 = User password does not expire */
107 #define ACB_AUTOLOCK   0x0400  /* 1 = Account auto locked */
108
109 static int pdb_decode_acct_ctrl(const char *p)
110 {
111         int acct_ctrl = 0;
112         int finished = 0;
113
114         /*
115          * Check if the account type bits have been encoded after the
116          * NT password (in the form [NDHTUWSLXI]).
117          */
118
119         if (*p != '[') return 0;
120
121         for (p++; *p && !finished; p++) {
122                 switch (*p) {
123                         case 'N': /* 'N'o password. */
124                           acct_ctrl |= ACB_PWNOTREQ;
125                           break;
126
127                         case 'D':  /* 'D'isabled. */
128                           acct_ctrl |= ACB_DISABLED ; 
129                           break;
130
131                         case 'H':  /* 'H'omedir required. */
132                           acct_ctrl |= ACB_HOMDIRREQ;
133                           break;
134
135                         case 'T': /* 'T'emp account. */
136                           acct_ctrl |= ACB_TEMPDUP;
137                           break;
138
139                         case 'U': /* 'U'ser account (normal). */
140                           acct_ctrl |= ACB_NORMAL;
141                           break;
142
143                         case 'M': /* 'M'NS logon user account. What is this? */
144                           acct_ctrl |= ACB_MNS;
145                           break;
146
147                         case 'W': /* 'W'orkstation account. */
148                           acct_ctrl |= ACB_WSTRUST;
149                           break;
150
151                         case 'S': /* 'S'erver account. */
152                           acct_ctrl |= ACB_SVRTRUST;
153                           break;
154
155                         case 'L': /* 'L'ocked account. */
156                           acct_ctrl |= ACB_AUTOLOCK;
157                           break;
158
159                         case 'X': /* No 'X'piry on password */
160                           acct_ctrl |= ACB_PWNOEXP;
161                           break;
162
163                         case 'I': /* 'I'nterdomain trust account. */
164                           acct_ctrl |= ACB_DOMTRUST;
165                           break;
166
167                         case ' ': /* ignore spaces */
168                           break;
169
170                         case ':':
171                         case '\n':
172                         case '\0': 
173                         case ']':
174                         default:
175                           finished = 1;
176                           break;
177                 }
178         }
179
180         return acct_ctrl;
181 }
182
183
184 /*
185  *      ntpwdhash converts Unicode password to 16-byte NT hash
186  *      with MD4
187  */
188 static void ntpwdhash (char *szHash, const char *szPassword)
189 {
190         char szUnicodePass[513];
191         char nPasswordLen;
192         int i;
193
194         /*
195          *      NT passwords are unicode.  Convert plain text password
196          *      to unicode by inserting a zero every other byte
197          */
198         nPasswordLen = strlen(szPassword);
199         for (i = 0; i < nPasswordLen; i++) {
200                 szUnicodePass[i << 1] = szPassword[i];
201                 szUnicodePass[(i << 1) + 1] = 0;
202         }
203
204         /* Encrypt Unicode password to a 16-byte MD4 hash */
205         md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
206 }
207
208
209 /*
210  *      challenge_hash() is used by mschap2() and auth_response()
211  *      implements RFC2759 ChallengeHash()
212  *      generates 64 bit challenge
213  */
214 static void challenge_hash( const char *peer_challenge,
215                             const char *auth_challenge,
216                             const char *user_name, char *challenge )
217 {
218         SHA1_CTX Context;
219         char hash[20];
220         
221         SHA1Init(&Context);
222         SHA1Update(&Context, peer_challenge, 16);
223         SHA1Update(&Context, auth_challenge, 16);
224         SHA1Update(&Context, user_name, strlen(user_name));
225         SHA1Final(hash, &Context);
226         memcpy(challenge, hash, 8);
227 }
228
229 static void mschap2(const char *peer_challenge, const char *auth_challenge,
230                     const char *user_name, const char *nt_password,
231                     char *response)
232 {
233         char challenge[8];
234         
235         challenge_hash(peer_challenge, auth_challenge, user_name,
236                        challenge);
237
238         lrad_mschap(nt_password, challenge, response);
239 }
240
241 /*
242  *      auth_response() generates MS-CHAP v2 SUCCESS response
243  *      according to RFC 2759 GenerateAuthenticatorResponse()
244  *      returns 42-octet response string
245  */
246 static void auth_response(const char *username, const char *nt_password,
247                           char *ntresponse,
248                           char *peer_challenge, char *auth_challenge,
249                           char *response)
250 {
251         SHA1_CTX Context;
252         char hashhash[16];
253         const char magic1[39] =
254         {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
255          0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
256          0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
257          0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
258                                              
259         const char magic2[41] =
260         {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
261          0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
262          0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
263          0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
264          0x6E};
265
266         char challenge[8];
267         char digest[20];
268
269         /*
270          * Hash password hash into hashhash
271          */
272
273         md4_calc(hashhash, nt_password, 16);
274
275         SHA1Init(&Context);
276         SHA1Update(&Context, hashhash, 16);
277         SHA1Update(&Context, ntresponse, 24);
278         SHA1Update(&Context, magic1, 39);
279         SHA1Final(digest, &Context);
280         challenge_hash(peer_challenge, auth_challenge, username, challenge);
281         SHA1Init(&Context);
282         SHA1Update(&Context, digest, 20);
283         SHA1Update(&Context, challenge, 8);
284         SHA1Update(&Context, magic2, 41);
285         SHA1Final(digest, &Context);
286
287         /*
288          * Encode the value of 'Digest' as "S=" followed by
289          * 40 ASCII hexadecimal digits and return it in
290          * AuthenticatorResponse.
291          * For example,
292          *   "S=0123456789ABCDEF0123456789ABCDEF01234567"
293          */
294         response[0] = 'S';
295         response[1] = '=';
296         bin2hex(digest, response + 2, 20);
297 }
298
299 struct mschap_instance {
300         int use_mppe;
301         int require_encryption;
302         int require_strong;
303         char *passwd_file;
304         char *auth_type;
305 };
306
307 static CONF_PARSER module_config[] = {
308         /*
309          *      Cache the password by default.
310          */
311         { "use_mppe",    PW_TYPE_BOOLEAN,
312           offsetof(struct mschap_instance,use_mppe), NULL, "yes" },
313         { "require_encryption",    PW_TYPE_BOOLEAN,
314           offsetof(struct mschap_instance,require_encryption), NULL, "no" },
315         { "require_strong",    PW_TYPE_BOOLEAN,
316           offsetof(struct mschap_instance,require_strong), NULL, "no" },
317         { "passwd",   PW_TYPE_STRING_PTR,
318           offsetof(struct mschap_instance, passwd_file), NULL,  NULL },
319         { "authtype",   PW_TYPE_STRING_PTR,
320           offsetof(struct mschap_instance, auth_type), NULL,  NULL },
321         
322         { NULL, -1, 0, NULL, NULL }             /* end the list */
323 };
324
325 /*
326  *      deinstantiate module, free all memory allocated during
327  *      mschap_instantiate()
328  */
329 static int mschap_detach(void *instance){
330 #define inst ((struct mschap_instance *)instance)
331         if (inst->passwd_file) free(inst->passwd_file);
332         if (inst->auth_type) free(inst->auth_type);
333         free(instance);
334         return 0;
335 #undef inst
336 }
337         
338 /*
339  *      Create instance for our module. Allocate space for
340  *      instance structure and read configuration parameters
341  */
342 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
343 {
344         struct mschap_instance *inst;
345
346         inst = *instance = rad_malloc(sizeof(*inst));
347         if (!inst) {
348                 return -1;
349         }
350         memset(inst, 0, sizeof(*inst));
351
352         if (cf_section_parse(conf, inst, module_config) < 0) {
353                 free(inst);
354                 return -1;
355         }
356
357         /*
358          *      This module used to support SMB Password files, but it
359          *      made it too complicated.  If the user tries to
360          *      configure an SMB Password file, then die, with an
361          *      error message.
362          */
363         if (inst->passwd_file) {
364                 radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module.  Use rlm_passwd module instead");
365                 mschap_detach(inst);
366                 return -1;
367         }
368
369         return 0;
370 }
371
372 /*
373  *      add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
374  *      attribute to reply packet
375  */
376 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
377                       const char* name, const char* value, int len)
378 {
379         VALUE_PAIR *reply_attr;
380         reply_attr = pairmake(name, "", T_OP_EQ);
381         if (!reply_attr) {
382                 DEBUG("  rlm_mschap: Failed to create attribute %s: %s\n", name, librad_errstr);
383                 return;
384         }
385
386         reply_attr->strvalue[0] = ident;
387         memcpy(reply_attr->strvalue + 1, value, len);
388         reply_attr->length = len + 1;
389         pairadd(vp, reply_attr);
390 }
391
392 /*
393  *      Add MPPE attributes to the reply.
394  */
395 static void mppe_add_reply(VALUE_PAIR** vp,
396                            const char* name, const char* value, int len)
397 {
398        VALUE_PAIR *reply_attr;
399        reply_attr = pairmake(name, "", T_OP_EQ);
400        if (!reply_attr) {
401                DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
402                return;
403        }
404
405        memcpy(reply_attr->strvalue, value, len);
406        reply_attr->length = len;
407        pairadd(vp, reply_attr);
408 }
409
410 static const uint8_t SHSpad1[40] =
411                { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
415
416 static const uint8_t SHSpad2[40] =
417                { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
418                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
419                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
420                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
421
422 static const uint8_t magic1[27] =
423                { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
424                  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
425                  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
426
427 static const uint8_t magic2[84] =
428                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
429                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
430                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
431                  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
432                  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
433                  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
434                  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
435                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
436                  0x6b, 0x65, 0x79, 0x2e };
437
438 static const uint8_t magic3[84] =
439                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
440                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
441                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
442                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
443                  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
444                  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
445                  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
446                  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
447                  0x6b, 0x65, 0x79, 0x2e };
448
449
450 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
451                               uint8_t *masterkey)
452 {
453        uint8_t digest[20];
454        SHA1_CTX Context;
455
456        SHA1Init(&Context);
457        SHA1Update(&Context,nt_hashhash,16);
458        SHA1Update(&Context,nt_response,24);
459        SHA1Update(&Context,magic1,27);
460        SHA1Final(digest,&Context);
461
462        memcpy(masterkey,digest,16);
463 }
464
465
466 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
467                                        int keylen,int issend)
468 {
469        uint8_t digest[20];
470        const uint8_t *s;
471        SHA1_CTX Context;
472
473        memset(digest,0,20);
474
475        if(issend) {
476                s = magic3;
477        } else {
478                s = magic2;
479        }
480
481        SHA1Init(&Context);
482        SHA1Update(&Context,masterkey,16);
483        SHA1Update(&Context,SHSpad1,40);
484        SHA1Update(&Context,s,84);
485        SHA1Update(&Context,SHSpad2,40);
486        SHA1Final(digest,&Context);
487
488        memcpy(sesskey,digest,keylen);
489 }
490
491
492 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
493                                uint8_t *sendkey,uint8_t *recvkey)
494 {
495        uint8_t masterkey[16];
496
497        mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
498
499        mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
500        mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
501 }
502
503 /*
504  *      Generate MPPE keys.
505  */
506 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
507                                uint8_t *nt_hash,uint8_t *response,
508                                uint8_t *sendkey,uint8_t *recvkey)
509 {
510         uint8_t enckey1[16];
511         uint8_t enckey2[16];
512         /* uint8_t salt[2]; */
513         uint8_t nt_hashhash[16];
514
515         md4_calc(nt_hashhash,nt_hash,16);
516
517         mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
518
519         /*
520          *      dictionary.microsoft defines these attributes as
521          *      'encrypt=2'.  The functions in src/lib/radius.c will
522          *      take care of encrypting/decrypting them as appropriate,
523          *      so that we don't have to.
524          */
525         memcpy (sendkey, enckey1, 16);
526         memcpy (recvkey, enckey2, 16);
527 }
528
529
530 /*
531  *      mschap_authorize() - authorize user if we can authenticate
532  *      it later. Add Auth-Type attribute if present in module
533  *      configuration (usually Auth-Type must be "MS-CHAP")
534  */
535 static int mschap_authorize(void * instance, REQUEST *request)
536 {
537 #define inst ((struct mschap_instance *)instance)
538         VALUE_PAIR *challenge = NULL, *response = NULL;
539         VALUE_PAIR *vp;
540         const char *authtype_name = "MS-CHAP";
541
542         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
543         if (!challenge) {
544                 return RLM_MODULE_NOOP;
545         }
546                 
547         response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
548         if (!response)
549                 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
550
551         /*
552          *      Nothing we recognize.  Don't do anything.
553          */
554         if (!response) {
555                 DEBUG2("  rlm_mschap: Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
556                 return RLM_MODULE_NOOP;
557         }
558
559         /*
560          *      Choose MS-CHAP, or whatever else they told us to use.
561          */
562         if (inst->auth_type) {
563                 authtype_name = inst->auth_type;
564         }
565
566         DEBUG2("  rlm_mschap: Found MS-CHAP attributes.  Setting 'Auth-Type  = %s'", authtype_name);
567
568         /*
569          *      Set Auth-Type to MS-CHAP.  The authentication code
570          *      will take care of turning clear-text passwords into
571          *      NT/LM passwords.
572          */
573         pairdelete(&request->config_items, PW_AUTHTYPE);
574         vp = pairmake("Auth-Type", authtype_name, T_OP_EQ);
575         rad_assert(vp != NULL);
576         pairadd(&request->config_items, vp);
577
578         return RLM_MODULE_OK;
579 #undef inst
580 }
581
582 /*
583  *      mschap_authenticate() - authenticate user based on given
584  *      attributes and configuration.
585  *      We will try to find out password in configuration
586  *      or in configured passwd file.
587  *      If one is found we will check paraneters given by NAS.
588  *
589  *      If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have 
590  *      one of:
591  *              PAP:      PW_PASSWORD or
592  *              MS-CHAP:  PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
593  *              MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
594  *      In case of password mismatch or locked account we MAY return
595  *      PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
596  *      If MS-CHAP2 succeeds we MUST return
597  *      PW_MSCHAP2_SUCCESS
598  */
599 static int mschap_authenticate(void * instance, REQUEST *request)
600 {
601 #define inst ((struct mschap_instance *)instance)
602         VALUE_PAIR *challenge = NULL, *response = NULL;
603         VALUE_PAIR *password = NULL;
604         VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
605         VALUE_PAIR *username;
606         VALUE_PAIR *reply_attr;
607         uint8_t calculated[32];
608         uint8_t msch2resp[42];
609         uint8_t mppe_sendkey[34];
610         uint8_t mppe_recvkey[34];
611         int chap = 0;
612         
613         /*
614          *      Find the SMB-Account-Ctrl attribute, or the
615          *      SMB-Account-Ctrl-Text attribute.
616          */
617         smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
618         if (!smb_ctrl) {
619                 password = pairfind(request->config_items,
620                                     PW_SMB_ACCOUNT_CTRL_TEXT);
621                 if (password) {
622                         smb_ctrl = pairmake("SMB-Account-CTRL", "0", T_OP_SET);
623                         pairadd(&request->config_items, smb_ctrl);
624                         smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->strvalue);
625                 }
626         }
627
628         /*
629          *      We're configured to do MS-CHAP authentication.
630          *      and account control information exists.  Enforce it.
631          */
632         if (smb_ctrl) {
633                 /*
634                  *      Password is not required.
635                  */
636                 if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) {
637                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says no password is required.");
638                         return RLM_MODULE_OK;
639                 }
640         }
641
642         /*
643          *      Decide how to get the passwords.
644          */
645         password = pairfind(request->config_items, PW_PASSWORD);
646
647         /*
648          *      We need an LM-Password.
649          */
650         lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
651         if (lm_password) {
652                 /*
653                  *      Allow raw octets.
654                  */
655                 if ((lm_password->length == 16) ||
656                     ((lm_password->length == 32) &&
657                      (hex2bin(lm_password->strvalue,
658                               lm_password->strvalue, 16) == 16))) {
659                         DEBUG2("  rlm_mschap: Found LM-Password");
660                         lm_password->length = 16;
661
662                 } else {
663                         radlog(L_ERR, "rlm_mschap: Invalid LM-Password");
664                         lm_password = NULL;
665                 }
666
667         } else if (!password) {
668                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create LM-Password.");
669
670         } else {                /* there is a configured User-Password */
671                 lm_password = pairmake("LM-Password", "", T_OP_EQ);
672                 if (!lm_password) {
673                         radlog(L_ERR, "No memory");
674                 } else {
675                         lrad_lmpwdhash(password->strvalue,
676                                        lm_password->strvalue);
677                         lm_password->length = 16;
678                         pairadd(&request->config_items, lm_password);
679                 }
680         }
681
682         /*
683          *      We need an NT-Password.
684          */
685         nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
686         if (nt_password) {
687                 if ((nt_password->length == 16) ||
688                     ((nt_password->length == 32) &&
689                      (hex2bin(nt_password->strvalue,
690                               nt_password->strvalue, 16) == 16))) {
691                         DEBUG2("  rlm_mschap: Found NT-Password");
692                         nt_password->length = 16;
693
694                 } else {
695                         radlog(L_ERR, "rlm_mschap: Invalid NT-Password");
696                         nt_password = NULL;
697                 }
698         } else if (!password) {
699                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create NT-Password.");
700
701         } else {                /* there is a configured User-Password */
702                 nt_password = pairmake("NT-Password", "", T_OP_EQ);
703                 if (!nt_password) {
704                         radlog(L_ERR, "No memory");
705                 } else {
706                         ntpwdhash(nt_password->strvalue, password->strvalue);
707                         nt_password->length = 16;
708                         pairadd(&request->config_items, nt_password);
709                 }
710         }
711
712         /*
713          *      No NT or LM Passwords, die.
714          */
715         if (!lm_password && !nt_password) {
716                 DEBUG2("  rlm_mschap: No LM-Password or NT-Password attribute found.  Cannot perform MS-CHAP authentication.");
717                 return RLM_MODULE_FAIL;
718         }
719         
720         /*
721          *      We MAY be asked to take a User-Password attribute from
722          *      the packet, and compare it to passwords retrieved from
723          *      an SMB Password file.
724          */
725         password = pairfind(request->packet->vps, PW_PASSWORD);
726         if (password) {
727                 if (lm_password) {
728                         lrad_lmpwdhash(calculated,
729                                        password->strvalue);
730                         if (memcmp(calculated,
731                                    lm_password->strvalue, 16) == 0) {
732                                 DEBUG2("  rlm_mschap: User-Password matches LM-Password.");
733                                 return RLM_MODULE_OK;
734                         } else {
735                                 DEBUG2("  rlm_mschap: FAILED: User-Password does NOT match LM-Password.");
736                         }
737                 } else if (nt_password) {
738                         ntpwdhash(calculated, password->strvalue);
739                         if (memcmp(calculated,
740                                    nt_password->strvalue, 16) == 0) {
741                                 DEBUG2("  rlm_mschap: User-Password matches NT-Password.");
742                                 return RLM_MODULE_OK;
743                         } else {
744                                 DEBUG2("  rlm_mschap: FAILED: User-Password does NOT match NT-Password.");
745                         }
746                 }
747                 return RLM_MODULE_REJECT;
748         } /* compare User-Password in packet to configured NT/LM-Password */
749
750         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
751         if (!challenge) {
752                 DEBUG2("  rlm_mschap: No MS-CHAP-Challenge in the request");
753                 return RLM_MODULE_REJECT;
754         }
755
756         /*
757          *      We also require an MS-CHAP-Response.
758          */
759         response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
760         /*
761          *      MS-CHAP-Response, means MS-CHAPv1
762          */
763         if (response) {
764                 int offset;
765
766                 /*
767                  *      MS-CHAPv1 challenges are 8 octets.
768                  */
769                 if (challenge->length < 8) {
770                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
771                         return RLM_MODULE_INVALID;
772                 }
773         
774                 /*
775                  *      Responses are 50 octets.
776                  */
777                 if (response->length < 50) {
778                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
779                         return RLM_MODULE_INVALID;
780                 }
781             
782                 /*
783                  *      We are doing MS-CHAP.  Calculate the MS-CHAP
784                  *      response
785                  */
786                 if (response->strvalue[1] & 0x01) {
787                         DEBUG2("  rlm_mschap: doing MS-CHAPv1 with NT-Password");
788                         password = nt_password;
789                         offset = 26;
790                 } else {
791                         DEBUG2("  rlm_mschap: doing MS-CHAPv1 with LM-Password");
792                         password = lm_password;
793                         offset = 2;
794                 }
795
796                 /*
797                  *      No password configured.  Die.
798                  */
799                 if (!password) {
800                         DEBUG2("  rlm_mschap: FAILED: No NT/LM-Password");
801                         return RLM_MODULE_REJECT;
802                 }
803                 
804                 /*
805                  *      Calculate the expected response.
806                  */
807                 lrad_mschap(password->strvalue, challenge->strvalue,
808                             calculated);
809                 if (memcmp(response->strvalue + offset,
810                            calculated, 24) != 0) {
811                         DEBUG("  rlm_mschap: FAILED: MS-CHAP-Response is incorrect");
812                         return RLM_MODULE_FAIL;
813                 }
814
815                 chap = 1;
816
817         } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
818                 /*
819                  *      MS-CHAPv2 challenges are 16 octets.
820                  */
821                 if (challenge->length < 16) {
822                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
823                         return RLM_MODULE_INVALID;
824                 }
825         
826                 /*
827                  *      Responses are 50 octets.
828                  */
829                 if (response->length < 50) {
830                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
831                         return RLM_MODULE_INVALID;
832                 }
833
834                 /*
835                  *      We also require a User-Name
836                  */
837                 username = pairfind(request->packet->vps, PW_USER_NAME);
838                 if (!username) {
839                         radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2");
840                         return RLM_MODULE_INVALID;
841                 }
842             
843                 /*
844                  *      We are doing MS-CHAPv2
845                  *      We need NT hash for it to calculate response
846                  */
847                 if (!nt_password) {
848                         DEBUG2("  rlm_mschap: No NT-Password configured.  Cannot perform MS-CHAPv2 authentication.");
849                         return RLM_MODULE_INVALID;
850                 }
851
852                 DEBUG2("  rlm_mschap: doing MS-CHAPv2 with NT-Password");
853                 mschap2(response->strvalue + 2, challenge->strvalue,
854                         username->strvalue, nt_password->strvalue,
855                         calculated);
856                 if (memcmp(response->strvalue + 26, calculated, 24) != 0) {
857                         DEBUG2("  rlm_mschap: FAILED: MS-CHAP2-Response is incorrect");
858                         add_reply(&request->reply->vps, *response->strvalue,
859                                   "MS-CHAP-Error", "E=691 R=1", 9);
860                         return RLM_MODULE_REJECT;
861                 }
862
863                 auth_response(username->strvalue,
864                               nt_password->strvalue, calculated,
865                               response->strvalue + 2,
866                               challenge->strvalue,
867                               msch2resp);
868                 add_reply( &request->reply->vps, *response->strvalue,
869                            "MS-CHAP2-Success", msch2resp, 42);
870                 chap = 2;
871                 
872         } else {                /* Neither CHAPv1 or CHAPv2 response: die */
873                 radlog(L_AUTH, "rlm_mschap: No MS-CHAP response found");
874                 return RLM_MODULE_INVALID;
875         }
876
877         /*
878          *      We have a CHAP response, but the account may be
879          *      disabled.  Reject the user with the same error code
880          *      we use when their password is invalid.
881          */
882         if (smb_ctrl) {
883                 /*
884                  *      Account is disabled.
885                  *
886                  *      They're found, but they don't exist, so we
887                  *      return 'not found'.
888                  */
889                 if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) ||
890                     ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) {
891                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
892                         add_reply( &request->reply->vps, *response->strvalue,
893                                    "MS-CHAP-Error", "E=691 R=1", 9);
894                         return RLM_MODULE_NOTFOUND;
895                 }
896
897                 /*
898                  *      User is locked out.
899                  */
900                 if ((smb_ctrl->lvalue & ACB_AUTOLOCK) != 0) {
901                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is locked out.");
902                         add_reply( &request->reply->vps, *response->strvalue,
903                                    "MS-CHAP-Error", "E=647 R=0", 9);
904                         return RLM_MODULE_USERLOCK;
905                 }
906         }
907
908         /* now create MPPE attributes */
909         if (inst->use_mppe) {
910                 if (chap == 1){
911                         DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
912                         memset(mppe_sendkey, 0, 32);
913                         if (lm_password) {
914                                 memcpy(mppe_sendkey, lm_password->strvalue, 8);
915                         }
916
917                         if (nt_password) {
918                                 /* 
919                                  *      According to RFC 2548 we
920                                  *      should send NT hash.  But in
921                                  *      practice it doesn't work.
922                                  *      Instead, we should send nthashhash
923                                  *
924                                  *      This is an error on RFC 2548.
925                                  */
926                                 md4_calc(mppe_sendkey + 8,
927                                          nt_password->strvalue, 16);
928                                 mppe_add_reply(&request->reply->vps,
929                                                "MS-CHAP-MPPE-Keys",
930                                                mppe_sendkey, 32);
931                         }
932                 } else if (chap == 2) {
933                         DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
934                         mppe_chap2_gen_keys128(request->secret,
935                                                request->packet->vector,
936                                                nt_password->strvalue,
937                                                response->strvalue + 26,
938                                                mppe_sendkey, mppe_recvkey);
939                         
940                         mppe_add_reply(&request->reply->vps,
941                                        "MS-MPPE-Recv-Key",
942                                        mppe_recvkey, 16);
943                         mppe_add_reply(&request->reply->vps,
944                                        "MS-MPPE-Send-Key",
945                                        mppe_sendkey, 16);
946                         
947                 }
948                 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
949                                       (inst->require_encryption)? "0x00000002":"0x00000001",
950                                       T_OP_EQ);
951                 rad_assert(reply_attr != NULL);
952                 pairadd(&request->reply->vps, reply_attr);
953                 reply_attr = pairmake("MS-MPPE-Encryption-Types",
954                                       (inst->require_strong)? "0x00000004":"0x00000006",
955                                       T_OP_EQ);
956                 rad_assert(reply_attr != NULL);
957                 pairadd(&request->reply->vps, reply_attr);
958                 
959         } /* else we weren't asked to use MPPE */
960
961         return RLM_MODULE_OK;
962 #undef inst
963 }
964
965 module_t rlm_mschap = {
966   "MS-CHAP",
967   RLM_TYPE_THREAD_SAFE,         /* type */
968   NULL,                         /* initialize */
969   mschap_instantiate,           /* instantiation */
970   {
971           mschap_authenticate,  /* authenticate */
972           mschap_authorize,     /* authorize */
973           NULL,                 /* pre-accounting */
974           NULL,                 /* accounting */
975           NULL,                 /* checksimul */
976           NULL,                 /* pre-proxy */
977           NULL,                 /* post-proxy */
978           NULL                  /* post-auth */
979   },
980   mschap_detach,                /* detach */
981   NULL,                         /* destroy */
982 };