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