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