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