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