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