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