Use new library functions hex2bin/bin2hex
[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 #include        "libradius.h"
47
48 #include        <stdio.h>
49 #include        <stdlib.h>
50 #include        <string.h>
51 #include        <ctype.h>
52
53 #include        "radiusd.h"
54 #include        "modules.h"
55
56 #include        "md4.h"
57 #include        "md5.h"
58 #include        "sha1.h"
59 #include        "rad_assert.h"
60
61 #include        "smbdes.h"
62
63 static const char rcsid[] = "$Id$";
64
65
66 /* Allowable account control bits */
67 #define ACB_DISABLED   0x0001  /* 1 = User account disabled */
68 #define ACB_HOMDIRREQ  0x0002  /* 1 = Home directory required */
69 #define ACB_PWNOTREQ   0x0004  /* 1 = User password not required */
70 #define ACB_TEMPDUP    0x0008  /* 1 = Temporary duplicate account */
71 #define ACB_NORMAL     0x0010  /* 1 = Normal user account */
72 #define ACB_MNS        0x0020  /* 1 = MNS logon user account */
73 #define ACB_DOMTRUST   0x0040  /* 1 = Interdomain trust account */
74 #define ACB_WSTRUST    0x0080  /* 1 = Workstation trust account */
75 #define ACB_SVRTRUST   0x0100  /* 1 = Server trust account */
76 #define ACB_PWNOEXP    0x0200  /* 1 = User password does not expire */
77 #define ACB_AUTOLOCK   0x0400  /* 1 = Account auto locked */
78
79 static int pdb_decode_acct_ctrl(const char *p)
80 {
81         int acct_ctrl = 0;
82         int finished = 0;
83
84         /*
85          * Check if the account type bits have been encoded after the
86          * NT password (in the form [NDHTUWSLXI]).
87          */
88
89         if (*p != '[') return 0;
90
91         for (p++; *p && !finished; p++) {
92                 switch (*p) {
93                         case 'N': /* 'N'o password. */
94                           acct_ctrl |= ACB_PWNOTREQ;
95                           break;
96
97                         case 'D':  /* 'D'isabled. */
98                           acct_ctrl |= ACB_DISABLED ;
99                           break;
100
101                         case 'H':  /* 'H'omedir required. */
102                           acct_ctrl |= ACB_HOMDIRREQ;
103                           break;
104
105                         case 'T': /* 'T'emp account. */
106                           acct_ctrl |= ACB_TEMPDUP;
107                           break;
108
109                         case 'U': /* 'U'ser account (normal). */
110                           acct_ctrl |= ACB_NORMAL;
111                           break;
112
113                         case 'M': /* 'M'NS logon user account. What is this? */
114                           acct_ctrl |= ACB_MNS;
115                           break;
116
117                         case 'W': /* 'W'orkstation account. */
118                           acct_ctrl |= ACB_WSTRUST;
119                           break;
120
121                         case 'S': /* 'S'erver account. */
122                           acct_ctrl |= ACB_SVRTRUST;
123                           break;
124
125                         case 'L': /* 'L'ocked account. */
126                           acct_ctrl |= ACB_AUTOLOCK;
127                           break;
128
129                         case 'X': /* No 'X'piry on password */
130                           acct_ctrl |= ACB_PWNOEXP;
131                           break;
132
133                         case 'I': /* 'I'nterdomain trust account. */
134                           acct_ctrl |= ACB_DOMTRUST;
135                           break;
136
137                         case ' ': /* ignore spaces */
138                           break;
139
140                         case ':':
141                         case '\n':
142                         case '\0':
143                         case ']':
144                         default:
145                           finished = 1;
146                           break;
147                 }
148         }
149
150         return acct_ctrl;
151 }
152
153
154 /*
155  *      ntpwdhash converts Unicode password to 16-byte NT hash
156  *      with MD4
157  */
158 static void ntpwdhash (char *szHash, const char *szPassword)
159 {
160         char szUnicodePass[513];
161         int nPasswordLen;
162         int i;
163
164         /*
165          *      NT passwords are unicode.  Convert plain text password
166          *      to unicode by inserting a zero every other byte
167          */
168         nPasswordLen = strlen(szPassword);
169         for (i = 0; i < nPasswordLen; i++) {
170                 szUnicodePass[i << 1] = szPassword[i];
171                 szUnicodePass[(i << 1) + 1] = 0;
172         }
173
174         /* Encrypt Unicode password to a 16-byte MD4 hash */
175         md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
176 }
177
178
179 /*
180  *      challenge_hash() is used by mschap2() and auth_response()
181  *      implements RFC2759 ChallengeHash()
182  *      generates 64 bit challenge
183  */
184 static void challenge_hash( const char *peer_challenge,
185                             const char *auth_challenge,
186                             const char *user_name, char *challenge )
187 {
188         SHA1_CTX Context;
189         char hash[20];
190
191         SHA1Init(&Context);
192         SHA1Update(&Context, peer_challenge, 16);
193         SHA1Update(&Context, auth_challenge, 16);
194         SHA1Update(&Context, user_name, strlen(user_name));
195         SHA1Final(hash, &Context);
196         memcpy(challenge, hash, 8);
197 }
198
199 /*
200  *      auth_response() generates MS-CHAP v2 SUCCESS response
201  *      according to RFC 2759 GenerateAuthenticatorResponse()
202  *      returns 42-octet response string
203  */
204 static void auth_response(const char *username,
205                           const char *nt_hash_hash,
206                           char *ntresponse,
207                           char *peer_challenge, char *auth_challenge,
208                           char *response)
209 {
210         SHA1_CTX Context;
211         const char magic1[39] =
212         {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
213          0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
214          0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
215          0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
216
217         const char magic2[41] =
218         {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
219          0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
220          0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
221          0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
222          0x6E};
223
224         char challenge[8];
225         char digest[20];
226
227         SHA1Init(&Context);
228         SHA1Update(&Context, nt_hash_hash, 16);
229         SHA1Update(&Context, ntresponse, 24);
230         SHA1Update(&Context, magic1, 39);
231         SHA1Final(digest, &Context);
232         challenge_hash(peer_challenge, auth_challenge, username, challenge);
233         SHA1Init(&Context);
234         SHA1Update(&Context, digest, 20);
235         SHA1Update(&Context, challenge, 8);
236         SHA1Update(&Context, magic2, 41);
237         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         lrad_bin2hex(digest, response + 2, 20);
249 }
250
251
252 typedef struct rlm_mschap_t {
253         int use_mppe;
254         int require_encryption;
255         int require_strong;
256         int with_ntdomain_hack; /* this should be in another module */
257         char *passwd_file;
258         char *xlat_name;
259         char *auth_type;        /* I don't think this is needed... */
260         char *ntlm_auth;
261 } rlm_mschap_t;
262
263
264 /*
265  *      Does dynamic translation of strings.
266  *
267  *      Pulls NT-Response, LM-Response, or Challenge from MSCHAP
268  *      attributes.
269  */
270 static int mschap_xlat(void *instance, REQUEST *request,
271                        char *fmt, char *out, size_t outlen,
272                        RADIUS_ESCAPE_STRING func)
273 {
274         int             i, data_len;
275         uint8_t         *data = NULL;
276         uint8_t         buffer[32];
277         VALUE_PAIR      *user_name;
278         VALUE_PAIR      *chap_challenge, *response;
279         rlm_mschap_t    *inst = instance;
280
281         chap_challenge = response = NULL;
282
283         func = func;            /* -Wunused */
284
285         /*
286          *      Challenge means MS-CHAPv1 challenge, or
287          *      hash of MS-CHAPv2 challenge, and peer challenge.
288          */
289         if (strcasecmp(fmt, "Challenge") == 0) {
290                 chap_challenge = pairfind(request->packet->vps,
291                                           PW_MSCHAP_CHALLENGE);
292                 if (!chap_challenge) {
293                         DEBUG2("  rlm_mschap: No MS-CHAP-Challenge in the request.");
294                         return 0;
295                 }
296
297                 /*
298                  *      MS-CHAP-Challenges are 8 octets,
299                  *      for MS-CHAPv2
300                  */
301                 if (chap_challenge->length == 8) {
302                         DEBUG2(" mschap1: %02x", chap_challenge->strvalue[0]);
303                         data = chap_challenge->strvalue;
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->strvalue[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->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->strvalue;
345                                 }
346                         } else {
347                                 username_string = user_name->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->strvalue + 2,
356                                        chap_challenge->strvalue,
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->strvalue[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->strvalue + 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->strvalue[1] & 0x01) != 0) {
414                         DEBUG2("  rlm_mschap: No LM-Response in MS-CHAP-Response");
415                         return 0;
416                 }
417                 data = response->strvalue + 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->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->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->strvalue, '\\');
460                 if (p) {
461                         p++;    /* skip the backslash */
462                 } else {
463                         p = user_name->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 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->strvalue[0] = ident;
640         memcpy(reply_attr->strvalue + 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->strvalue, 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->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->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, *response = NULL;
973         VALUE_PAIR *password = NULL;
974         VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
975         VALUE_PAIR *username;
976         VALUE_PAIR *reply_attr;
977         uint8_t nthashhash[16];
978         uint8_t msch2resp[42];
979         char *username_string;
980         int chap = 0;
981
982         /*
983          *      Find the SMB-Account-Ctrl attribute, or the
984          *      SMB-Account-Ctrl-Text attribute.
985          */
986         smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
987         if (!smb_ctrl) {
988                 password = pairfind(request->config_items,
989                                     PW_SMB_ACCOUNT_CTRL_TEXT);
990                 if (password) {
991                         smb_ctrl = pairmake("SMB-Account-CTRL", "0", T_OP_SET);
992                         pairadd(&request->config_items, smb_ctrl);
993                         smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->strvalue);
994                 }
995         }
996
997         /*
998          *      We're configured to do MS-CHAP authentication.
999          *      and account control information exists.  Enforce it.
1000          */
1001         if (smb_ctrl) {
1002                 /*
1003                  *      Password is not required.
1004                  */
1005                 if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) {
1006                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says no password is required.");
1007                         return RLM_MODULE_OK;
1008                 }
1009         }
1010
1011         /*
1012          *      Decide how to get the passwords.
1013          */
1014         password = pairfind(request->config_items, PW_PASSWORD);
1015
1016         /*
1017          *      We need an LM-Password.
1018          */
1019         lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
1020         if (lm_password) {
1021                 /*
1022                  *      Allow raw octets.
1023                  */
1024                 if ((lm_password->length == 16) ||
1025                     ((lm_password->length == 32) &&
1026                      (lrad_hex2bin(lm_password->strvalue,
1027                                    lm_password->strvalue, 16) == 16))) {
1028                         DEBUG2("  rlm_mschap: Found LM-Password");
1029                         lm_password->length = 16;
1030
1031                 } else {
1032                         radlog(L_ERR, "rlm_mschap: Invalid LM-Password");
1033                         lm_password = NULL;
1034                 }
1035
1036         } else if (!password) {
1037                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create LM-Password.");
1038
1039         } else {                /* there is a configured User-Password */
1040                 lm_password = pairmake("LM-Password", "", T_OP_EQ);
1041                 if (!lm_password) {
1042                         radlog(L_ERR, "No memory");
1043                 } else {
1044                         smbdes_lmpwdhash(password->strvalue,
1045                                        lm_password->strvalue);
1046                         lm_password->length = 16;
1047                         pairadd(&request->config_items, lm_password);
1048                 }
1049         }
1050
1051         /*
1052          *      We need an NT-Password.
1053          */
1054         nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
1055         if (nt_password) {
1056                 if ((nt_password->length == 16) ||
1057                     ((nt_password->length == 32) &&
1058                      (lrad_hex2bin(nt_password->strvalue,
1059                                    nt_password->strvalue, 16) == 16))) {
1060                         DEBUG2("  rlm_mschap: Found NT-Password");
1061                         nt_password->length = 16;
1062
1063                 } else {
1064                         radlog(L_ERR, "rlm_mschap: Invalid NT-Password");
1065                         nt_password = NULL;
1066                 }
1067         } else if (!password) {
1068                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create NT-Password.");
1069
1070         } else {                /* there is a configured User-Password */
1071                 nt_password = pairmake("NT-Password", "", T_OP_EQ);
1072                 if (!nt_password) {
1073                         radlog(L_ERR, "No memory");
1074                         return RLM_MODULE_FAIL;
1075                 } else {
1076                         ntpwdhash(nt_password->strvalue, password->strvalue);
1077                         nt_password->length = 16;
1078                         pairadd(&request->config_items, nt_password);
1079                 }
1080         }
1081
1082         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
1083         if (!challenge) {
1084                 DEBUG2("  rlm_mschap: No MS-CHAP-Challenge in the request");
1085                 return RLM_MODULE_REJECT;
1086         }
1087
1088         /*
1089          *      We also require an MS-CHAP-Response.
1090          */
1091         response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
1092
1093         /*
1094          *      MS-CHAP-Response, means MS-CHAPv1
1095          */
1096         if (response) {
1097                 int offset;
1098
1099                 /*
1100                  *      MS-CHAPv1 challenges are 8 octets.
1101                  */
1102                 if (challenge->length < 8) {
1103                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
1104                         return RLM_MODULE_INVALID;
1105                 }
1106
1107                 /*
1108                  *      Responses are 50 octets.
1109                  */
1110                 if (response->length < 50) {
1111                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
1112                         return RLM_MODULE_INVALID;
1113                 }
1114
1115                 /*
1116                  *      We are doing MS-CHAP.  Calculate the MS-CHAP
1117                  *      response
1118                  */
1119                 if (response->strvalue[1] & 0x01) {
1120                         DEBUG2("  rlm_mschap: Told to do MS-CHAPv1 with NT-Password");
1121                         password = nt_password;
1122                         offset = 26;
1123                 } else {
1124                         DEBUG2("  rlm_mschap: Told to do MS-CHAPv1 with LM-Password");
1125                         password = lm_password;
1126                         offset = 2;
1127                 }
1128
1129                 /*
1130                  *      Do the MS-CHAP authentication.
1131                  */
1132                 if (do_mschap(inst, request, password, challenge->strvalue,
1133                               response->strvalue + offset, nthashhash) < 0) {
1134                         DEBUG2("  rlm_mschap: MS-CHAP-Response is incorrect.");
1135                         add_reply(&request->reply->vps, *response->strvalue,
1136                                   "MS-CHAP-Error", "E=691 R=1", 9);
1137                         return RLM_MODULE_REJECT;
1138                 }
1139
1140                 chap = 1;
1141
1142         } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
1143                 uint8_t mschapv1_challenge[16];
1144
1145                 /*
1146                  *      MS-CHAPv2 challenges are 16 octets.
1147                  */
1148                 if (challenge->length < 16) {
1149                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
1150                         return RLM_MODULE_INVALID;
1151                 }
1152
1153                 /*
1154                  *      Responses are 50 octets.
1155                  */
1156                 if (response->length < 50) {
1157                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
1158                         return RLM_MODULE_INVALID;
1159                 }
1160
1161                 /*
1162                  *      We also require a User-Name
1163                  */
1164                 username = pairfind(request->packet->vps, PW_USER_NAME);
1165                 if (!username) {
1166                         radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2");
1167                         return RLM_MODULE_INVALID;
1168                 }
1169
1170
1171                 /*
1172                  *      with_ntdomain_hack moved here
1173                  */
1174                 if ((username_string = strchr(username->strvalue, '\\')) != NULL) {
1175                         if (inst->with_ntdomain_hack) {
1176                                 username_string++;
1177                         } else {
1178                                 DEBUG2("  rlm_mschap: NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
1179                                 username_string = username->strvalue;
1180                         }
1181                 } else {
1182                         username_string = username->strvalue;
1183                 }
1184
1185                 /*
1186                  *      The old "mschapv2" function has been moved to
1187                  *      here.
1188                  *
1189                  *      MS-CHAPv2 takes some additional data to create an
1190                  *      MS-CHAPv1 challenge, and then does MS-CHAPv1.
1191                  */
1192                 challenge_hash(response->strvalue + 2, /* peer challenge */
1193                                challenge->strvalue, /* our challenge */
1194                                username_string, /* user name */
1195                                mschapv1_challenge); /* resulting challenge */
1196                 
1197                 DEBUG2("  rlm_mschap: Told to do MS-CHAPv2 for %s with NT-Password",
1198                        username_string);
1199
1200                 if (do_mschap(inst, request, nt_password, mschapv1_challenge,
1201                               response->strvalue + 26, nthashhash) < 0) {
1202                         DEBUG2("  rlm_mschap: FAILED: MS-CHAP2-Response is incorrect");
1203                         add_reply(&request->reply->vps, *response->strvalue,
1204                                   "MS-CHAP-Error", "E=691 R=1", 9);
1205                         return RLM_MODULE_REJECT;
1206                 }
1207
1208                 /*
1209                  *      Get the NT-hash-hash, if necessary
1210                  */
1211                 if (nt_password) {
1212                 }
1213
1214                 auth_response(username_string, /* without the domain */
1215                               nthashhash, /* nt-hash-hash */
1216                               response->strvalue + 26, /* peer response */
1217                               response->strvalue + 2, /* peer challenge */
1218                               challenge->strvalue, /* our challenge */
1219                               msch2resp); /* calculated MPPE key */
1220                 add_reply( &request->reply->vps, *response->strvalue,
1221                            "MS-CHAP2-Success", msch2resp, 42);
1222                 chap = 2;
1223
1224         } else {                /* Neither CHAPv1 or CHAPv2 response: die */
1225                 radlog(L_AUTH, "rlm_mschap: No MS-CHAP response found");
1226                 return RLM_MODULE_INVALID;
1227         }
1228
1229         /*
1230          *      We have a CHAP response, but the account may be
1231          *      disabled.  Reject the user with the same error code
1232          *      we use when their password is invalid.
1233          */
1234         if (smb_ctrl) {
1235                 /*
1236                  *      Account is disabled.
1237                  *
1238                  *      They're found, but they don't exist, so we
1239                  *      return 'not found'.
1240                  */
1241                 if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) ||
1242                     ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) {
1243                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
1244                         add_reply( &request->reply->vps, *response->strvalue,
1245                                    "MS-CHAP-Error", "E=691 R=1", 9);
1246                         return RLM_MODULE_NOTFOUND;
1247                 }
1248
1249                 /*
1250                  *      User is locked out.
1251                  */
1252                 if ((smb_ctrl->lvalue & ACB_AUTOLOCK) != 0) {
1253                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is locked out.");
1254                         add_reply( &request->reply->vps, *response->strvalue,
1255                                    "MS-CHAP-Error", "E=647 R=0", 9);
1256                         return RLM_MODULE_USERLOCK;
1257                 }
1258         }
1259
1260         /* now create MPPE attributes */
1261         if (inst->use_mppe) {
1262                 uint8_t mppe_sendkey[34];
1263                 uint8_t mppe_recvkey[34];
1264
1265                 if (chap == 1){
1266                         DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
1267                         memset(mppe_sendkey, 0, 32);
1268                         if (lm_password) {
1269                                 memcpy(mppe_sendkey, lm_password->strvalue, 8);
1270                         }
1271
1272                         /*
1273                          *      According to RFC 2548 we
1274                          *      should send NT hash.  But in
1275                          *      practice it doesn't work.
1276                          *      Instead, we should send nthashhash
1277                          *
1278                          *      This is an error on RFC 2548.
1279                          */
1280                         /*
1281                          *      do_mschap cares to zero nthashhash if NT hash
1282                          *      is not available.
1283                          */
1284                         memcpy(mppe_sendkey + 8,
1285                                nthashhash, 16);
1286                         mppe_add_reply(&request->reply->vps,
1287                                        "MS-CHAP-MPPE-Keys",
1288                                        mppe_sendkey, 32);
1289                 } else if (chap == 2) {
1290                         DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
1291                         mppe_chap2_gen_keys128(nthashhash,
1292                                                response->strvalue + 26,
1293                                                mppe_sendkey, mppe_recvkey);
1294                         
1295                         mppe_add_reply(&request->reply->vps,
1296                                        "MS-MPPE-Recv-Key",
1297                                        mppe_recvkey, 16);
1298                         mppe_add_reply(&request->reply->vps,
1299                                        "MS-MPPE-Send-Key",
1300                                        mppe_sendkey, 16);
1301
1302                 }
1303                 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
1304                                       (inst->require_encryption)? "0x00000002":"0x00000001",
1305                                       T_OP_EQ);
1306                 rad_assert(reply_attr != NULL);
1307                 pairadd(&request->reply->vps, reply_attr);
1308                 reply_attr = pairmake("MS-MPPE-Encryption-Types",
1309                                       (inst->require_strong)? "0x00000004":"0x00000006",
1310                                       T_OP_EQ);
1311                 rad_assert(reply_attr != NULL);
1312                 pairadd(&request->reply->vps, reply_attr);
1313
1314         } /* else we weren't asked to use MPPE */
1315
1316         return RLM_MODULE_OK;
1317 #undef inst
1318 }
1319
1320 module_t rlm_mschap = {
1321   "MS-CHAP",
1322   RLM_TYPE_THREAD_SAFE,         /* type */
1323   NULL,                         /* initialize */
1324   mschap_instantiate,           /* instantiation */
1325   {
1326           mschap_authenticate,  /* authenticate */
1327           mschap_authorize,     /* authorize */
1328           NULL,                 /* pre-accounting */
1329           NULL,                 /* accounting */
1330           NULL,                 /* checksimul */
1331           NULL,                 /* pre-proxy */
1332           NULL,                 /* post-proxy */
1333           NULL                  /* post-auth */
1334   },
1335   mschap_detach,                /* detach */
1336   NULL,                         /* destroy */
1337 };