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