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