import from HEAD:
[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 #include        "smbdes.h"
62
63 static const char rcsid[] = "$Id$";
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                 switch (*p) {
125                         case 'N': /* 'N'o password. */
126                           acct_ctrl |= ACB_PWNOTREQ;
127                           break;
128
129                         case 'D':  /* 'D'isabled. */
130                           acct_ctrl |= ACB_DISABLED ;
131                           break;
132
133                         case 'H':  /* 'H'omedir required. */
134                           acct_ctrl |= ACB_HOMDIRREQ;
135                           break;
136
137                         case 'T': /* 'T'emp account. */
138                           acct_ctrl |= ACB_TEMPDUP;
139                           break;
140
141                         case 'U': /* 'U'ser account (normal). */
142                           acct_ctrl |= ACB_NORMAL;
143                           break;
144
145                         case 'M': /* 'M'NS logon user account. What is this? */
146                           acct_ctrl |= ACB_MNS;
147                           break;
148
149                         case 'W': /* 'W'orkstation account. */
150                           acct_ctrl |= ACB_WSTRUST;
151                           break;
152
153                         case 'S': /* 'S'erver account. */
154                           acct_ctrl |= ACB_SVRTRUST;
155                           break;
156
157                         case 'L': /* 'L'ocked account. */
158                           acct_ctrl |= ACB_AUTOLOCK;
159                           break;
160
161                         case 'X': /* No 'X'piry on password */
162                           acct_ctrl |= ACB_PWNOEXP;
163                           break;
164
165                         case 'I': /* 'I'nterdomain trust account. */
166                           acct_ctrl |= ACB_DOMTRUST;
167                           break;
168
169                         case ' ': /* ignore spaces */
170                           break;
171
172                         case ':':
173                         case '\n':
174                         case '\0':
175                         case ']':
176                         default:
177                           finished = 1;
178                           break;
179                 }
180         }
181
182         return acct_ctrl;
183 }
184
185
186 /*
187  *      ntpwdhash converts Unicode password to 16-byte NT hash
188  *      with MD4
189  */
190 static void ntpwdhash (unsigned char *szHash, const char *szPassword)
191 {
192         char szUnicodePass[513];
193         int nPasswordLen;
194         int i;
195
196         /*
197          *      NT passwords are unicode.  Convert plain text password
198          *      to unicode by inserting a zero every other byte
199          */
200         nPasswordLen = strlen(szPassword);
201         for (i = 0; i < nPasswordLen; i++) {
202                 szUnicodePass[i << 1] = szPassword[i];
203                 szUnicodePass[(i << 1) + 1] = 0;
204         }
205
206         /* Encrypt Unicode password to a 16-byte MD4 hash */
207         md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
208 }
209
210
211 /*
212  *      challenge_hash() is used by mschap2() and auth_response()
213  *      implements RFC2759 ChallengeHash()
214  *      generates 64 bit challenge
215  */
216 static void challenge_hash( const char *peer_challenge,
217                             const char *auth_challenge,
218                             const char *user_name, char *challenge )
219 {
220         SHA1_CTX Context;
221         unsigned char hash[20];
222
223         SHA1Init(&Context);
224         SHA1Update(&Context, peer_challenge, 16);
225         SHA1Update(&Context, auth_challenge, 16);
226         SHA1Update(&Context, user_name, strlen(user_name));
227         SHA1Final(hash, &Context);
228         memcpy(challenge, hash, 8);
229 }
230
231 /*
232  *      auth_response() generates MS-CHAP v2 SUCCESS response
233  *      according to RFC 2759 GenerateAuthenticatorResponse()
234  *      returns 42-octet response string
235  */
236 static void auth_response(const char *username,
237                           const unsigned char *nt_hash_hash,
238                           unsigned char *ntresponse,
239                           char *peer_challenge, char *auth_challenge,
240                           char *response)
241 {
242         SHA1_CTX Context;
243         const unsigned char magic1[39] =
244         {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
245          0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
246          0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
247          0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
248
249         const unsigned char magic2[41] =
250         {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
251          0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
252          0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
253          0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
254          0x6E};
255
256         char challenge[8];
257         unsigned char digest[20];
258
259         SHA1Init(&Context);
260         SHA1Update(&Context, nt_hash_hash, 16);
261         SHA1Update(&Context, ntresponse, 24);
262         SHA1Update(&Context, magic1, 39);
263         SHA1Final(digest, &Context);
264         challenge_hash(peer_challenge, auth_challenge, username, challenge);
265         SHA1Init(&Context);
266         SHA1Update(&Context, digest, 20);
267         SHA1Update(&Context, challenge, 8);
268         SHA1Update(&Context, magic2, 41);
269         SHA1Final(digest, &Context);
270
271         /*
272          *      Encode the value of 'Digest' as "S=" followed by
273          *      40 ASCII hexadecimal digits and return it in
274          *      AuthenticatorResponse.
275          *      For example,
276          *      "S=0123456789ABCDEF0123456789ABCDEF01234567"
277          */
278         response[0] = 'S';
279         response[1] = '=';
280         bin2hex(digest, response + 2, 20);
281 }
282
283
284 typedef struct rlm_mschap_t {
285         int use_mppe;
286         int require_encryption;
287         int require_strong;
288         int with_ntdomain_hack; /* this should be in another module */
289         char *passwd_file;
290         char *xlat_name;
291         char *ntlm_auth;
292         char *auth_type;
293 } rlm_mschap_t;
294
295
296 /*
297  *      Does dynamic translation of strings.
298  *
299  *      Pulls NT-Response, LM-Response, or Challenge from MSCHAP
300  *      attributes.
301  */
302 static int mschap_xlat(void *instance, REQUEST *request,
303                        char *fmt, char *out, size_t outlen,
304                        RADIUS_ESCAPE_STRING func)
305 {
306         int             i, data_len;
307         uint8_t         *data = NULL;
308         uint8_t         buffer[32];
309         VALUE_PAIR      *user_name;
310         VALUE_PAIR      *chap_challenge, *response;
311         rlm_mschap_t    *inst = instance;
312
313         chap_challenge = response = NULL;
314
315         func = func;            /* -Wunused */
316
317         /*
318          *      Challenge means MS-CHAPv1 challenge, or
319          *      hash of MS-CHAPv2 challenge, and peer challenge.
320          */
321         if (strcasecmp(fmt, "Challenge") == 0) {
322                 chap_challenge = pairfind(request->packet->vps,
323                                           PW_MSCHAP_CHALLENGE);
324                 if (!chap_challenge) {
325                         DEBUG2("  rlm_mschap: No MS-CHAP-Challenge in the request.");
326                         return 0;
327                 }
328
329                 /*
330                  *      MS-CHAP-Challenges are 8 octets,
331                  *      for MS-CHAPv2
332                  */
333                 if (chap_challenge->length == 8) {
334                         DEBUG2(" mschap1: %02x", chap_challenge->strvalue[0]);
335                         data = chap_challenge->strvalue;
336                         data_len = 8;
337
338                         /*
339                          *      MS-CHAP-Challenges are 16 octets,
340                          *      for MS-CHAPv2.
341                          */
342                 } else if (chap_challenge->length == 16) {
343                         char *username_string;
344
345                         DEBUG2(" mschap2: %02x", chap_challenge->strvalue[0]);
346                         response = pairfind(request->packet->vps,
347                                             PW_MSCHAP2_RESPONSE);
348                         if (!response) {
349                                 DEBUG2("  rlm_mschap: MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge.");
350                                 return 0;
351                         }
352
353                         /*
354                          *      Responses are 50 octets.
355                          */
356                         if (response->length < 50) {
357                                 radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
358                                 return 0;
359                         }
360
361                         user_name = pairfind(request->packet->vps,
362                                              PW_USER_NAME);
363                         if (!user_name) {
364                                 DEBUG2("  rlm_mschap: User-Name is required to calculateMS-CHAPv1 Challenge.");
365                                 return 0;
366                         }
367
368                         /*
369                          *      with_ntdomain_hack moved here, too.
370                          */
371                         if ((username_string = strchr(user_name->strvalue, '\\')) != NULL) {
372                                 if (inst->with_ntdomain_hack) {
373                                         username_string++;
374                                 } else {
375                                         DEBUG2("  rlm_mschap: NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
376                                         username_string = user_name->strvalue;
377                                 }
378                         } else {
379                                 username_string = user_name->strvalue;
380                         }
381
382                         /*
383                          *      Get the MS-CHAPv1 challenge
384                          *      from the MS-CHAPv2 peer challenge,
385                          *      our challenge, and the user name.
386                          */
387                         challenge_hash(response->strvalue + 2,
388                                        chap_challenge->strvalue,
389                                        username_string, buffer);
390                         data = buffer;
391                         data_len = 8;
392                 } else {
393                         DEBUG2("  rlm_mschap: Invalid MS-CHAP challenge length");
394                         return 0;
395                 }
396                 
397                 /*
398                  *      Get the MS-CHAPv1 response, or the MS-CHAPv2
399                  *      response.
400                  */
401         } else if (strcasecmp(fmt, "NT-Response") == 0) {
402                 response = pairfind(request->packet->vps,
403                                     PW_MSCHAP_RESPONSE);
404                 if (!response) response = pairfind(request->packet->vps,
405                                                    PW_MSCHAP2_RESPONSE);
406                 if (!response) {
407                         DEBUG2("  rlm_mschap: No MS-CHAP-Response or MS-CHAP2-Response was found in the request.");
408                         return 0;
409                 }
410
411                 /*
412                  *      For MS-CHAPv1, the NT-Response exists only
413                  *      if the second octet says so.
414                  */
415                 if ((response->attribute == PW_MSCHAP_RESPONSE) &&
416                     ((response->strvalue[1] & 0x01) == 0)) {
417                         DEBUG2("  rlm_mschap: No NT-Response in MS-CHAP-Response");
418                         return 0;
419                 }
420
421                 /*
422                  *      MS-CHAP-Response and MS-CHAP2-Response have
423                  *      the NT-Response at the same offset, and are
424                  *      the same length.
425                  */
426                 data = response->strvalue + 26;
427                 data_len = 24;
428                 
429                 /*
430                  *      LM-Response is deprecated, and exists only
431                  *      in MS-CHAPv1, and not often there.
432                  */
433         } else if (strcasecmp(fmt, "LM-Response") == 0) {
434                 response = pairfind(request->packet->vps,
435                                     PW_MSCHAP_RESPONSE);
436                 if (!response) {
437                         DEBUG2("  rlm_mschap: No MS-CHAP-Response was found in the request.");
438                         return 0;
439                 }
440
441                 /*
442                  *      For MS-CHAPv1, the NT-Response exists only
443                  *      if the second octet says so.
444                  */
445                 if ((response->strvalue[1] & 0x01) != 0) {
446                         DEBUG2("  rlm_mschap: No LM-Response in MS-CHAP-Response");
447                         return 0;
448                 }
449                 data = response->strvalue + 2;
450                 data_len = 24;
451
452                 /*
453                  *      Pull the NT-Domain out of the User-Name, if it exists.
454                  */
455         } else if (strcasecmp(fmt, "NT-Domain") == 0) {
456                 char *p, *q;
457
458                 user_name = pairfind(request->packet->vps, PW_USER_NAME);
459                 if (!user_name) {
460                         DEBUG2("  rlm_mschap: No User-Name was found in the request.");
461                         return 0;
462                 }
463                 
464                 /*
465                  *      First check to see if this is a host/ style User-Name
466                  *      (a la Kerberos host principal)
467                  */
468                 if (strncmp(user_name->strvalue, "host/", 5) == 0) {
469                         /*
470                          *      If we're getting a User-Name formatted in this way,
471                          *      it's likely due to PEAP.  The Windows Domain will be
472                          *      the first domain component following the hostname,
473                          *      or the machine name itself if only a hostname is supplied
474                          */
475                         p = strchr(user_name->strvalue, '.');
476                         if (!p) {
477                                 DEBUG2("  rlm_mschap: setting NT-Domain to same as machine name");
478                                 strNcpy(out, user_name->strvalue + 5, outlen);
479                         } else {
480                                 p++;    /* skip the period */
481                                 q = strchr(p, '.');
482                                 /*
483                                  * use the same hack as below
484                                  * only if another period was found
485                                  */
486                                 if (q) *q = '\0';
487                                 strNcpy(out, p, outlen);
488                                 if (q) *q = '.';
489                         }
490                 } else {
491                         p = strchr(user_name->strvalue, '\\');
492                         if (!p) {
493                                 DEBUG2("  rlm_mschap: No NT-Domain was found in the User-Name.");
494                                 return 0;
495                         }
496
497                         /*
498                          *      Hack.  This is simpler than the alternatives.
499                          */
500                         *p = '\0';
501                         strNcpy(out, user_name->strvalue, outlen);
502                         *p = '\\';
503                 }
504
505                 return strlen(out);
506
507                 /*
508                  *      Pull the User-Name out of the User-Name...
509                  */
510         } else if (strcasecmp(fmt, "User-Name") == 0) {
511                 char *p;
512
513                 user_name = pairfind(request->packet->vps, PW_USER_NAME);
514                 if (!user_name) {
515                         DEBUG2("  rlm_mschap: No User-Name was found in the request.");
516                         return 0;
517                 }
518                 
519                 /*
520                  *      First check to see if this is a host/ style User-Name
521                  *      (a la Kerberos host principal)
522                  */
523                 if (strncmp(user_name->strvalue, "host/", 5) == 0) {
524                         /*
525                          *      If we're getting a User-Name formatted in this way,
526                          *      it's likely due to PEAP.  When authenticating this against
527                          *      a Domain, Windows will expect the User-Name to be in the
528                          *      format of hostname$, the SAM version of the name, so we
529                          *      have to convert it to that here.  We do so by stripping
530                          *      off the first 5 characters (host/), and copying everything
531                          *      from that point to the first period into a string and appending
532                          *      a $ to the end.
533                          */
534                         p = strchr(user_name->strvalue, '.');
535                         /*
536                          * use the same hack as above
537                          * only if a period was found
538                          */
539                         if (p) *p = '\0';
540                         snprintf(out, outlen, "%s$", user_name->strvalue + 5);
541                         if (p) *p = '.';
542                 } else {
543                         p = strchr(user_name->strvalue, '\\');
544                         if (p) {
545                                 p++;    /* skip the backslash */
546                         } else {
547                                 p = user_name->strvalue; /* use the whole User-Name */
548                         }
549                         strNcpy(out, p, outlen);
550                 }
551
552                 return strlen(out);
553
554                 /*
555                  * Return the NT-Hash of the passed string
556                  */
557         } else if (strncasecmp(fmt, "NT-Hash ", 8) == 0) {
558                 char *p;
559
560                 p = fmt + 8;    /* 7 is the length of 'NT-Hash' */
561                 if ((p == '\0')  || (outlen <= 32))
562                         return 0;
563                 DEBUG("rlm_mschap: NT-Hash: %s",p);
564                 ntpwdhash(buffer,p);
565
566                 lrad_bin2hex(buffer, out, 16);
567                 out[32] = '\0';
568                 DEBUG("rlm_mschap: NT-Hash: Result: %s",out);
569                 return 32;
570
571                 /*
572                  * Return the LM-Hash of the passed string
573                  */
574         } else if (strncasecmp(fmt, "LM-Hash ", 8) == 0) {
575                 char *p;
576
577                 p = fmt + 8;    /* 7 is the length of 'LM-Hash' */
578                 if ((p == '\0') || (outlen <= 32))
579                         return 0;
580                         
581                 DEBUG("rlm_mschap: LM-Hash: %s",p);
582                 smbdes_lmpwdhash(p,buffer);
583                 lrad_bin2hex(buffer, out, 16);
584                 out[32] = '\0';
585                 DEBUG("rlm_mschap: LM-Hash: Result: %s",out);
586                 return 32;
587         } else {
588                 DEBUG2("  rlm_mschap: Unknown expansion string \"%s\"",
589                        fmt);
590                 return 0;
591         }
592
593         if (outlen == 0) return 0; /* nowhere to go, don't do anything */
594
595         /*
596          *      Didn't set anything: this is bad.
597          */
598         if (!data) {
599                 DEBUG2("  rlm_mschap: Failed to do anything intelligent");
600                 return 0;
601         }
602
603         /*
604          *      Check the output length.
605          */
606         if (outlen < ((data_len * 2) + 1)) {
607                 data_len = (outlen - 1) / 2;
608         }
609
610         /*
611          *      
612          */
613         for (i = 0; i < data_len; i++) {
614                 sprintf(out + (2 * i), "%02x", data[i]);
615         }
616         out[data_len * 2] = '\0';
617         
618         return data_len * 2;
619 }
620
621
622 static CONF_PARSER module_config[] = {
623         /*
624          *      Cache the password by default.
625          */
626         { "use_mppe",    PW_TYPE_BOOLEAN,
627           offsetof(rlm_mschap_t,use_mppe), NULL, "yes" },
628         { "require_encryption",    PW_TYPE_BOOLEAN,
629           offsetof(rlm_mschap_t,require_encryption), NULL, "no" },
630         { "require_strong",    PW_TYPE_BOOLEAN,
631           offsetof(rlm_mschap_t,require_strong), NULL, "no" },
632         { "with_ntdomain_hack",     PW_TYPE_BOOLEAN,
633           offsetof(rlm_mschap_t,with_ntdomain_hack), NULL, "no" },
634         { "passwd",   PW_TYPE_STRING_PTR,
635           offsetof(rlm_mschap_t, passwd_file), NULL,  NULL },
636         { "ntlm_auth",   PW_TYPE_STRING_PTR,
637           offsetof(rlm_mschap_t, ntlm_auth), NULL,  NULL },
638
639         { NULL, -1, 0, NULL, NULL }             /* end the list */
640 };
641
642 /*
643  *      deinstantiate module, free all memory allocated during
644  *      mschap_instantiate()
645  */
646 static int mschap_detach(void *instance){
647 #define inst ((rlm_mschap_t *)instance)
648         free(inst->passwd_file);
649         free(inst->ntlm_auth);
650         if (inst->xlat_name) {
651                 xlat_unregister(inst->xlat_name, mschap_xlat);
652                 free(inst->xlat_name);
653         }
654         free(instance);
655         return 0;
656 #undef inst
657 }
658
659 /*
660  *      Create instance for our module. Allocate space for
661  *      instance structure and read configuration parameters
662  */
663 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
664 {
665         const char *xlat_name;
666         rlm_mschap_t *inst;
667
668         inst = *instance = rad_malloc(sizeof(*inst));
669         if (!inst) {
670                 return -1;
671         }
672         memset(inst, 0, sizeof(*inst));
673
674         if (cf_section_parse(conf, inst, module_config) < 0) {
675                 free(inst);
676                 return -1;
677         }
678
679         /*
680          *      This module used to support SMB Password files, but it
681          *      made it too complicated.  If the user tries to
682          *      configure an SMB Password file, then die, with an
683          *      error message.
684          */
685         if (inst->passwd_file) {
686                 radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module.  Use rlm_passwd module instead");
687                 mschap_detach(inst);
688                 return -1;
689         }
690
691         /*
692          *      Create the dynamic translation.
693          */
694         xlat_name = cf_section_name2(conf);
695         if (xlat_name == NULL)
696                 xlat_name = cf_section_name1(conf);
697         if (xlat_name){
698                 inst->xlat_name = strdup(xlat_name);
699                 xlat_register(xlat_name, mschap_xlat, inst);
700         }
701
702         /*
703          *      For backwards compatibility
704          */
705         if (!dict_valbyname(PW_AUTH_TYPE, inst->xlat_name)) {
706                 inst->auth_type = "MS-CHAP";
707         }
708
709         return 0;
710 }
711
712 /*
713  *      add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
714  *      attribute to reply packet
715  */
716 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
717                       const char* name, const char* value, int len)
718 {
719         VALUE_PAIR *reply_attr;
720         reply_attr = pairmake(name, "", T_OP_EQ);
721         if (!reply_attr) {
722                 DEBUG("  rlm_mschap: Failed to create attribute %s: %s\n", name, librad_errstr);
723                 return;
724         }
725
726         reply_attr->strvalue[0] = ident;
727         memcpy(reply_attr->strvalue + 1, value, len);
728         reply_attr->length = len + 1;
729         pairadd(vp, reply_attr);
730 }
731
732 /*
733  *      Add MPPE attributes to the reply.
734  */
735 static void mppe_add_reply(VALUE_PAIR **vp,
736                            const char* name, const char* value, int len)
737 {
738        VALUE_PAIR *reply_attr;
739        reply_attr = pairmake(name, "", T_OP_EQ);
740        if (!reply_attr) {
741                DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
742                return;
743        }
744
745        memcpy(reply_attr->strvalue, value, len);
746        reply_attr->length = len;
747        pairadd(vp, reply_attr);
748 }
749
750
751 /*
752  *      Do the MS-CHAP stuff.
753  *
754  *      This function is here so that all of the MS-CHAP related
755  *      authentication is in one place, and we can perhaps later replace
756  *      it with code to call winbindd, or something similar.
757  */
758 static int do_mschap(rlm_mschap_t *inst,
759                      REQUEST *request, VALUE_PAIR *password,
760                      uint8_t *challenge, uint8_t *response,
761                      uint8_t *nthashhash)
762 {
763         int             do_ntlm_auth = 0;
764         uint8_t         calculated[24];
765         VALUE_PAIR      *vp = NULL;
766
767         /*
768          *      If we have ntlm_auth configured, use it unless told
769          *      otherwise
770          */
771         if (inst->ntlm_auth) do_ntlm_auth = 1;
772
773         /*
774          *      If we have an ntlm_auth configuration, then we may
775          *      want to use it.
776          */
777         vp = pairfind(request->config_items,
778                       PW_MS_CHAP_USE_NTLM_AUTH);
779         if (vp) do_ntlm_auth = vp->lvalue;
780
781         /*
782          *      No ntlm_auth configured, attribute to tell us to
783          *      use it exists, and we're told to use it.  We don't
784          *      know what to do...
785          */
786         if (!inst->ntlm_auth && do_ntlm_auth) {
787                 DEBUG2("  rlm_mschap: Asked to use ntlm_auth, but it was not configured in the mschap{} section.");
788                 return -1;
789         }
790
791         /*
792          *      Do normal authentication.
793          */
794         if (!do_ntlm_auth) {
795                 /*
796                  *      No password: can't do authentication.
797                  */
798                 if (!password) {
799                         DEBUG2("  rlm_mschap: FAILED: No NT/LM-Password.  Cannot perform authentication.");
800                         return -1;
801                 }
802                 
803                 smbdes_mschap(password->strvalue, challenge, calculated);
804                 if (memcmp(response, calculated, 24) != 0) {
805                         return -1;
806                 }
807                 
808                 /*
809                  *      If the password exists, and is an NT-Password,
810                  *      then calculate the hash of the NT hash.  Doing this
811                  *      here minimizes work for later.
812                  */
813                 if (password && (password->attribute == PW_NT_PASSWORD)) {
814                         md4_calc(nthashhash, password->strvalue, 16);
815                 } else {
816                         memset(nthashhash, 0, 16);
817                 }
818         } else {                /* run ntlm_auth */
819                 int     result;
820                 char    buffer[256];
821
822                 memset(nthashhash, 0, 16);
823
824                 /*
825                  *      Run the program, and expect that we get 16 
826                  */
827                 result = radius_exec_program(inst->ntlm_auth, request,
828                                              TRUE, /* wait */
829                                              buffer, sizeof(buffer),
830                                              NULL, NULL);
831                 if (result != 0) {
832                         char *p;
833                         
834                         DEBUG2("  rlm_mschap: External script failed.");
835
836                         vp = pairmake("Module-Failure-Message", "", T_OP_EQ);
837                         if (!vp) {
838                                 radlog(L_ERR, "No memory");
839                                 return -1;
840                         }
841
842                         p = strchr(buffer, '\n');
843                         if (p) *p = '\0';
844                         snprintf(vp->strvalue, sizeof(vp->strvalue), 
845                                  "rlm_mschap: %s", buffer);
846                         vp->length = strlen(vp->strvalue);
847                         pairadd(&request->packet->vps, vp);
848                         return -1;
849                 }
850
851                 /*
852                  *      Parse the answer as an nthashhash.
853                  *
854                  *      ntlm_auth currently returns:
855                  *      NT_KEY: 000102030405060708090a0b0c0d0e0f
856                  */
857                 if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
858                         DEBUG2("  rlm_mschap: Invalid output from ntlm_auth: expecting NT_KEY");
859                         return -1;
860                 }
861
862                 /*
863                  *      Check the length.  It should be at least 32,
864                  *      with an LF at the end.
865                  */
866                 if (strlen(buffer + 8) < 32) {
867                         DEBUG2("  rlm_mschap: Invalid output from ntlm_auth: NT_KEY has unexpected length");
868                         return -1;
869                 }
870
871                 /*
872                  *      Update the NT hash hash, from the NT key.
873                  */
874                 if (hex2bin(buffer + 8, nthashhash, 16) != 16) {
875                         DEBUG2("  rlm_mschap: Invalid output from ntlm_auth: NT_KEY has non-hex values");
876                         return -1;
877                 }
878         }
879
880         return 0;
881 }
882
883
884 /*
885  *      Data for the hashes.
886  */
887 static const uint8_t SHSpad1[40] =
888                { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
889                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
890                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
891                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
892
893 static const uint8_t SHSpad2[40] =
894                { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
895                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
896                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
897                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
898
899 static const uint8_t magic1[27] =
900                { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
901                  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
902                  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
903
904 static const uint8_t magic2[84] =
905                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
906                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
907                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
908                  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
909                  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
910                  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
911                  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
912                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
913                  0x6b, 0x65, 0x79, 0x2e };
914
915 static const uint8_t magic3[84] =
916                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
917                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
918                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
919                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
920                  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
921                  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
922                  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
923                  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
924                  0x6b, 0x65, 0x79, 0x2e };
925
926
927 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
928                               uint8_t *masterkey)
929 {
930        uint8_t digest[20];
931        SHA1_CTX Context;
932
933        SHA1Init(&Context);
934        SHA1Update(&Context,nt_hashhash,16);
935        SHA1Update(&Context,nt_response,24);
936        SHA1Update(&Context,magic1,27);
937        SHA1Final(digest,&Context);
938
939        memcpy(masterkey,digest,16);
940 }
941
942
943 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
944                                        int keylen,int issend)
945 {
946        uint8_t digest[20];
947        const uint8_t *s;
948        SHA1_CTX Context;
949
950        memset(digest,0,20);
951
952        if(issend) {
953                s = magic3;
954        } else {
955                s = magic2;
956        }
957
958        SHA1Init(&Context);
959        SHA1Update(&Context,masterkey,16);
960        SHA1Update(&Context,SHSpad1,40);
961        SHA1Update(&Context,s,84);
962        SHA1Update(&Context,SHSpad2,40);
963        SHA1Final(digest,&Context);
964
965        memcpy(sesskey,digest,keylen);
966 }
967
968
969 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
970                                    uint8_t *sendkey,uint8_t *recvkey)
971 {
972        uint8_t masterkey[16];
973
974        mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
975
976        mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
977        mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
978 }
979
980 /*
981  *      Generate MPPE keys.
982  */
983 static void mppe_chap2_gen_keys128(uint8_t *nt_hashhash,uint8_t *response,
984                                    uint8_t *sendkey,uint8_t *recvkey)
985 {
986         uint8_t enckey1[16];
987         uint8_t enckey2[16];
988
989         mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
990
991         /*
992          *      dictionary.microsoft defines these attributes as
993          *      'encrypt=2'.  The functions in src/lib/radius.c will
994          *      take care of encrypting/decrypting them as appropriate,
995          *      so that we don't have to.
996          */
997         memcpy (sendkey, enckey1, 16);
998         memcpy (recvkey, enckey2, 16);
999 }
1000
1001
1002 /*
1003  *      mschap_authorize() - authorize user if we can authenticate
1004  *      it later. Add Auth-Type attribute if present in module
1005  *      configuration (usually Auth-Type must be "MS-CHAP")
1006  */
1007 static int mschap_authorize(void * instance, REQUEST *request)
1008 {
1009 #define inst ((rlm_mschap_t *)instance)
1010         VALUE_PAIR *challenge = NULL;
1011         VALUE_PAIR *response = NULL;
1012         VALUE_PAIR *vp;
1013
1014         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
1015         if (!challenge) {
1016                 return RLM_MODULE_NOOP;
1017         }
1018
1019         response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
1020         if (!response)
1021                 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
1022
1023         /*
1024          *      Nothing we recognize.  Don't do anything.
1025          */
1026         if (!response) {
1027                 DEBUG2("  rlm_mschap: Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
1028                 return RLM_MODULE_NOOP;
1029         }
1030
1031         DEBUG2("  rlm_mschap: Found MS-CHAP attributes.  Setting 'Auth-Type  = %s'", inst->xlat_name);
1032
1033         /*
1034          *      Set Auth-Type to MS-CHAP.  The authentication code
1035          *      will take care of turning clear-text passwords into
1036          *      NT/LM passwords.
1037          */
1038         vp = pairmake("Auth-Type", inst->auth_type, T_OP_EQ);
1039         if (!vp) return RLM_MODULE_FAIL;
1040         pairmove(&request->config_items, &vp);
1041         pairfree(&vp);          /* may be NULL */
1042
1043         return RLM_MODULE_OK;
1044 #undef inst
1045 }
1046
1047 /*
1048  *      mschap_authenticate() - authenticate user based on given
1049  *      attributes and configuration.
1050  *      We will try to find out password in configuration
1051  *      or in configured passwd file.
1052  *      If one is found we will check paraneters given by NAS.
1053  *
1054  *      If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
1055  *      one of:
1056  *              PAP:      PW_PASSWORD or
1057  *              MS-CHAP:  PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
1058  *              MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
1059  *      In case of password mismatch or locked account we MAY return
1060  *      PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
1061  *      If MS-CHAP2 succeeds we MUST return
1062  *      PW_MSCHAP2_SUCCESS
1063  */
1064 static int mschap_authenticate(void * instance, REQUEST *request)
1065 {
1066 #define inst ((rlm_mschap_t *)instance)
1067         VALUE_PAIR *challenge = NULL;
1068         VALUE_PAIR *response = NULL;
1069         VALUE_PAIR *password = NULL;
1070         VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
1071         VALUE_PAIR *username;
1072         VALUE_PAIR *reply_attr;
1073         uint8_t nthashhash[16];
1074         uint8_t msch2resp[42];
1075         char *username_string;
1076         int chap = 0;
1077
1078         /*
1079          *      Find the SMB-Account-Ctrl attribute, or the
1080          *      SMB-Account-Ctrl-Text attribute.
1081          */
1082         smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
1083         if (!smb_ctrl) {
1084                 password = pairfind(request->config_items,
1085                                     PW_SMB_ACCOUNT_CTRL_TEXT);
1086                 if (password) {
1087                         smb_ctrl = pairmake("SMB-Account-CTRL", "0", T_OP_SET);
1088                         pairadd(&request->config_items, smb_ctrl);
1089                         smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->strvalue);
1090                 }
1091         }
1092
1093         /*
1094          *      We're configured to do MS-CHAP authentication.
1095          *      and account control information exists.  Enforce it.
1096          */
1097         if (smb_ctrl) {
1098                 /*
1099                  *      Password is not required.
1100                  */
1101                 if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) {
1102                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says no password is required.");
1103                         return RLM_MODULE_OK;
1104                 }
1105         }
1106
1107         /*
1108          *      Decide how to get the passwords.
1109          */
1110         password = pairfind(request->config_items, PW_PASSWORD);
1111
1112         /*
1113          *      We need an LM-Password.
1114          */
1115         lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
1116         if (lm_password) {
1117                 /*
1118                  *      Allow raw octets.
1119                  */
1120                 if ((lm_password->length == 16) ||
1121                     ((lm_password->length == 32) &&
1122                      (hex2bin(lm_password->strvalue,
1123                               lm_password->strvalue, 16) == 16))) {
1124                         DEBUG2("  rlm_mschap: Found LM-Password");
1125                         lm_password->length = 16;
1126
1127                 } else {
1128                         radlog(L_ERR, "rlm_mschap: Invalid LM-Password");
1129                         lm_password = NULL;
1130                 }
1131
1132         } else if (!password) {
1133                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create LM-Password.");
1134
1135         } else {                /* there is a configured User-Password */
1136                 lm_password = pairmake("LM-Password", "", T_OP_EQ);
1137                 if (!lm_password) {
1138                         radlog(L_ERR, "No memory");
1139                 } else {
1140                         smbdes_lmpwdhash(password->strvalue,
1141                                        lm_password->strvalue);
1142                         lm_password->length = 16;
1143                         pairadd(&request->config_items, lm_password);
1144                 }
1145         }
1146
1147         /*
1148          *      We need an NT-Password.
1149          */
1150         nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
1151         if (nt_password) {
1152                 if ((nt_password->length == 16) ||
1153                     ((nt_password->length == 32) &&
1154                      (hex2bin(nt_password->strvalue,
1155                               nt_password->strvalue, 16) == 16))) {
1156                         DEBUG2("  rlm_mschap: Found NT-Password");
1157                         nt_password->length = 16;
1158
1159                 } else {
1160                         radlog(L_ERR, "rlm_mschap: Invalid NT-Password");
1161                         nt_password = NULL;
1162                 }
1163         } else if (!password) {
1164                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create NT-Password.");
1165
1166         } else {                /* there is a configured User-Password */
1167                 nt_password = pairmake("NT-Password", "", T_OP_EQ);
1168                 if (!nt_password) {
1169                         radlog(L_ERR, "No memory");
1170                         return RLM_MODULE_FAIL;
1171                 } else {
1172                         ntpwdhash(nt_password->strvalue, password->strvalue);
1173                         nt_password->length = 16;
1174                         pairadd(&request->config_items, nt_password);
1175                 }
1176         }
1177
1178         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
1179         if (!challenge) {
1180                 DEBUG2("  rlm_mschap: No MS-CHAP-Challenge in the request");
1181                 return RLM_MODULE_REJECT;
1182         }
1183
1184         /*
1185          *      We also require an MS-CHAP-Response.
1186          */
1187         response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
1188
1189         /*
1190          *      MS-CHAP-Response, means MS-CHAPv1
1191          */
1192         if (response) {
1193                 int offset;
1194
1195                 /*
1196                  *      MS-CHAPv1 challenges are 8 octets.
1197                  */
1198                 if (challenge->length < 8) {
1199                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
1200                         return RLM_MODULE_INVALID;
1201                 }
1202
1203                 /*
1204                  *      Responses are 50 octets.
1205                  */
1206                 if (response->length < 50) {
1207                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
1208                         return RLM_MODULE_INVALID;
1209                 }
1210
1211                 /*
1212                  *      We are doing MS-CHAP.  Calculate the MS-CHAP
1213                  *      response
1214                  */
1215                 if (response->strvalue[1] & 0x01) {
1216                         DEBUG2("  rlm_mschap: Told to do MS-CHAPv1 with NT-Password");
1217                         password = nt_password;
1218                         offset = 26;
1219                 } else {
1220                         DEBUG2("  rlm_mschap: Told to do MS-CHAPv1 with LM-Password");
1221                         password = lm_password;
1222                         offset = 2;
1223                 }
1224
1225                 /*
1226                  *      Do the MS-CHAP authentication.
1227                  */
1228                 if (do_mschap(inst, request, password, challenge->strvalue,
1229                               response->strvalue + offset, nthashhash) < 0) {
1230                         DEBUG2("  rlm_mschap: MS-CHAP-Response is incorrect.");
1231                         add_reply(&request->reply->vps, *response->strvalue,
1232                                   "MS-CHAP-Error", "E=691 R=1", 9);
1233                         return RLM_MODULE_REJECT;
1234                 }
1235
1236                 chap = 1;
1237
1238         } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
1239                 uint8_t mschapv1_challenge[16];
1240
1241                 /*
1242                  *      MS-CHAPv2 challenges are 16 octets.
1243                  */
1244                 if (challenge->length < 16) {
1245                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
1246                         return RLM_MODULE_INVALID;
1247                 }
1248
1249                 /*
1250                  *      Responses are 50 octets.
1251                  */
1252                 if (response->length < 50) {
1253                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
1254                         return RLM_MODULE_INVALID;
1255                 }
1256
1257                 /*
1258                  *      We also require a User-Name
1259                  */
1260                 username = pairfind(request->packet->vps, PW_USER_NAME);
1261                 if (!username) {
1262                         radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2");
1263                         return RLM_MODULE_INVALID;
1264                 }
1265
1266
1267                 /*
1268                  *      with_ntdomain_hack moved here
1269                  */
1270                 if ((username_string = strchr(username->strvalue, '\\')) != NULL) {
1271                         if (inst->with_ntdomain_hack) {
1272                                 username_string++;
1273                         } else {
1274                                 DEBUG2("  rlm_mschap: NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
1275                                 username_string = username->strvalue;
1276                         }
1277                 } else {
1278                         username_string = username->strvalue;
1279                 }
1280
1281                 /*
1282                  *      The old "mschapv2" function has been moved to
1283                  *      here.
1284                  *
1285                  *      MS-CHAPv2 takes some additional data to create an
1286                  *      MS-CHAPv1 challenge, and then does MS-CHAPv1.
1287                  */
1288                 challenge_hash(response->strvalue + 2, /* peer challenge */
1289                                challenge->strvalue, /* our challenge */
1290                                username_string, /* user name */
1291                                mschapv1_challenge); /* resulting challenge */
1292                 
1293                 DEBUG2("  rlm_mschap: Told to do MS-CHAPv2 for %s with NT-Password",
1294                        username_string);
1295
1296                 if (do_mschap(inst, request, nt_password, mschapv1_challenge,
1297                               response->strvalue + 26, nthashhash) < 0) {
1298                         DEBUG2("  rlm_mschap: FAILED: MS-CHAP2-Response is incorrect");
1299                         add_reply(&request->reply->vps, *response->strvalue,
1300                                   "MS-CHAP-Error", "E=691 R=1", 9);
1301                         return RLM_MODULE_REJECT;
1302                 }
1303
1304                 /*
1305                  *      Get the NT-hash-hash, if necessary
1306                  */
1307                 if (nt_password) {
1308                 }
1309
1310                 auth_response(username_string, /* without the domain */
1311                               nthashhash, /* nt-hash-hash */
1312                               response->strvalue + 26, /* peer response */
1313                               response->strvalue + 2, /* peer challenge */
1314                               challenge->strvalue, /* our challenge */
1315                               msch2resp); /* calculated MPPE key */
1316                 add_reply( &request->reply->vps, *response->strvalue,
1317                            "MS-CHAP2-Success", msch2resp, 42);
1318                 chap = 2;
1319
1320         } else {                /* Neither CHAPv1 or CHAPv2 response: die */
1321                 radlog(L_AUTH, "rlm_mschap: No MS-CHAP response found");
1322                 return RLM_MODULE_INVALID;
1323         }
1324
1325         /*
1326          *      We have a CHAP response, but the account may be
1327          *      disabled.  Reject the user with the same error code
1328          *      we use when their password is invalid.
1329          */
1330         if (smb_ctrl) {
1331                 /*
1332                  *      Account is disabled.
1333                  *
1334                  *      They're found, but they don't exist, so we
1335                  *      return 'not found'.
1336                  */
1337                 if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) ||
1338                     ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) {
1339                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
1340                         add_reply( &request->reply->vps, *response->strvalue,
1341                                    "MS-CHAP-Error", "E=691 R=1", 9);
1342                         return RLM_MODULE_NOTFOUND;
1343                 }
1344
1345                 /*
1346                  *      User is locked out.
1347                  */
1348                 if ((smb_ctrl->lvalue & ACB_AUTOLOCK) != 0) {
1349                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is locked out.");
1350                         add_reply( &request->reply->vps, *response->strvalue,
1351                                    "MS-CHAP-Error", "E=647 R=0", 9);
1352                         return RLM_MODULE_USERLOCK;
1353                 }
1354         }
1355
1356         /* now create MPPE attributes */
1357         if (inst->use_mppe) {
1358                 uint8_t mppe_sendkey[34];
1359                 uint8_t mppe_recvkey[34];
1360
1361                 if (chap == 1){
1362                         DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
1363                         memset(mppe_sendkey, 0, 32);
1364                         if (lm_password) {
1365                                 memcpy(mppe_sendkey, lm_password->strvalue, 8);
1366                         }
1367
1368                         /*
1369                          *      According to RFC 2548 we
1370                          *      should send NT hash.  But in
1371                          *      practice it doesn't work.
1372                          *      Instead, we should send nthashhash
1373                          *
1374                          *      This is an error on RFC 2548.
1375                          */
1376                         /*
1377                          *      do_mschap cares to zero nthashhash if NT hash
1378                          *      is not available.
1379                          */
1380                         memcpy(mppe_sendkey + 8,
1381                                nthashhash, 16);
1382                         mppe_add_reply(&request->reply->vps,
1383                                        "MS-CHAP-MPPE-Keys",
1384                                        mppe_sendkey, 32);
1385                 } else if (chap == 2) {
1386                         DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
1387                         mppe_chap2_gen_keys128(nthashhash,
1388                                                response->strvalue + 26,
1389                                                mppe_sendkey, mppe_recvkey);
1390                         
1391                         mppe_add_reply(&request->reply->vps,
1392                                        "MS-MPPE-Recv-Key",
1393                                        mppe_recvkey, 16);
1394                         mppe_add_reply(&request->reply->vps,
1395                                        "MS-MPPE-Send-Key",
1396                                        mppe_sendkey, 16);
1397
1398                 }
1399                 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
1400                                       (inst->require_encryption)? "0x00000002":"0x00000001",
1401                                       T_OP_EQ);
1402                 rad_assert(reply_attr != NULL);
1403                 pairadd(&request->reply->vps, reply_attr);
1404                 reply_attr = pairmake("MS-MPPE-Encryption-Types",
1405                                       (inst->require_strong)? "0x00000004":"0x00000006",
1406                                       T_OP_EQ);
1407                 rad_assert(reply_attr != NULL);
1408                 pairadd(&request->reply->vps, reply_attr);
1409
1410         } /* else we weren't asked to use MPPE */
1411
1412         return RLM_MODULE_OK;
1413 #undef inst
1414 }
1415
1416 module_t rlm_mschap = {
1417   "MS-CHAP",
1418   RLM_TYPE_THREAD_SAFE,         /* type */
1419   NULL,                         /* initialize */
1420   mschap_instantiate,           /* instantiation */
1421   {
1422           mschap_authenticate,  /* authenticate */
1423           mschap_authorize,     /* authorize */
1424           NULL,                 /* pre-accounting */
1425           NULL,                 /* accounting */
1426           NULL,                 /* checksimul */
1427           NULL,                 /* pre-proxy */
1428           NULL,                 /* post-proxy */
1429           NULL                  /* post-auth */
1430   },
1431   mschap_detach,                /* detach */
1432   NULL,                         /* destroy */
1433 };