perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[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 static const char rcsid[] = "$Id$";
62
63 static const char *letters = "0123456789ABCDEF";
64
65 /*
66  *      hex2bin converts hexadecimal strings into binary
67  */
68 static int hex2bin (const char *szHex, unsigned char* szBin, int len)
69 {
70         char * c1, * c2;
71         int i;
72
73         for (i = 0; i < len; i++) {
74                 if( !(c1 = memchr(letters, toupper((int) szHex[i << 1]), 16)) ||
75                     !(c2 = memchr(letters, toupper((int) szHex[(i << 1) + 1]), 16)))
76                      break;
77                  szBin[i] = ((c1-letters)<<4) + (c2-letters);
78         }
79         return i;
80 }
81
82 /*
83  *      bin2hex creates hexadecimal presentation
84  *      of binary data
85  */
86 static void bin2hex (const unsigned char *szBin, char *szHex, int len)
87 {
88         int i;
89         for (i = 0; i < len; i++) {
90                 szHex[i<<1] = letters[szBin[i] >> 4];
91                 szHex[(i<<1) + 1] = letters[szBin[i] & 0x0F];
92         }
93 }
94
95
96 /* Allowable account control bits */
97 #define ACB_DISABLED   0x0001  /* 1 = User account disabled */
98 #define ACB_HOMDIRREQ  0x0002  /* 1 = Home directory required */
99 #define ACB_PWNOTREQ   0x0004  /* 1 = User password not required */
100 #define ACB_TEMPDUP    0x0008  /* 1 = Temporary duplicate account */
101 #define ACB_NORMAL     0x0010  /* 1 = Normal user account */
102 #define ACB_MNS        0x0020  /* 1 = MNS logon user account */
103 #define ACB_DOMTRUST   0x0040  /* 1 = Interdomain trust account */
104 #define ACB_WSTRUST    0x0080  /* 1 = Workstation trust account */
105 #define ACB_SVRTRUST   0x0100  /* 1 = Server trust account */
106 #define ACB_PWNOEXP    0x0200  /* 1 = User password does not expire */
107 #define ACB_AUTOLOCK   0x0400  /* 1 = Account auto locked */
108
109 static int pdb_decode_acct_ctrl(const char *p)
110 {
111         int acct_ctrl = 0;
112         int finished = 0;
113
114         /*
115          * Check if the account type bits have been encoded after the
116          * NT password (in the form [NDHTUWSLXI]).
117          */
118
119         if (*p != '[') return 0;
120
121         for (p++; *p && !finished; p++) {
122                 switch (*p) {
123                         case 'N': /* 'N'o password. */
124                           acct_ctrl |= ACB_PWNOTREQ;
125                           break;
126
127                         case 'D':  /* 'D'isabled. */
128                           acct_ctrl |= ACB_DISABLED ;
129                           break;
130
131                         case 'H':  /* 'H'omedir required. */
132                           acct_ctrl |= ACB_HOMDIRREQ;
133                           break;
134
135                         case 'T': /* 'T'emp account. */
136                           acct_ctrl |= ACB_TEMPDUP;
137                           break;
138
139                         case 'U': /* 'U'ser account (normal). */
140                           acct_ctrl |= ACB_NORMAL;
141                           break;
142
143                         case 'M': /* 'M'NS logon user account. What is this? */
144                           acct_ctrl |= ACB_MNS;
145                           break;
146
147                         case 'W': /* 'W'orkstation account. */
148                           acct_ctrl |= ACB_WSTRUST;
149                           break;
150
151                         case 'S': /* 'S'erver account. */
152                           acct_ctrl |= ACB_SVRTRUST;
153                           break;
154
155                         case 'L': /* 'L'ocked account. */
156                           acct_ctrl |= ACB_AUTOLOCK;
157                           break;
158
159                         case 'X': /* No 'X'piry on password */
160                           acct_ctrl |= ACB_PWNOEXP;
161                           break;
162
163                         case 'I': /* 'I'nterdomain trust account. */
164                           acct_ctrl |= ACB_DOMTRUST;
165                           break;
166
167                         case ' ': /* ignore spaces */
168                           break;
169
170                         case ':':
171                         case '\n':
172                         case '\0':
173                         case ']':
174                         default:
175                           finished = 1;
176                           break;
177                 }
178         }
179
180         return acct_ctrl;
181 }
182
183
184 /*
185  *      ntpwdhash converts Unicode password to 16-byte NT hash
186  *      with MD4
187  */
188 static void ntpwdhash (char *szHash, const char *szPassword)
189 {
190         char szUnicodePass[513];
191         char nPasswordLen;
192         int i;
193
194         /*
195          *      NT passwords are unicode.  Convert plain text password
196          *      to unicode by inserting a zero every other byte
197          */
198         nPasswordLen = strlen(szPassword);
199         for (i = 0; i < nPasswordLen; i++) {
200                 szUnicodePass[i << 1] = szPassword[i];
201                 szUnicodePass[(i << 1) + 1] = 0;
202         }
203
204         /* Encrypt Unicode password to a 16-byte MD4 hash */
205         md4_calc(szHash, szUnicodePass, (nPasswordLen<<1) );
206 }
207
208
209 /*
210  *      challenge_hash() is used by mschap2() and auth_response()
211  *      implements RFC2759 ChallengeHash()
212  *      generates 64 bit challenge
213  */
214 static void challenge_hash( const char *peer_challenge,
215                             const char *auth_challenge,
216                             const char *user_name, char *challenge )
217 {
218         SHA1_CTX Context;
219         char hash[20];
220
221         SHA1Init(&Context);
222         SHA1Update(&Context, peer_challenge, 16);
223         SHA1Update(&Context, auth_challenge, 16);
224         SHA1Update(&Context, user_name, strlen(user_name));
225         SHA1Final(hash, &Context);
226         memcpy(challenge, hash, 8);
227 }
228
229 static void mschap2(const char *peer_challenge, const char *auth_challenge,
230                     const char *user_name, const char *nt_password,
231                     char *response)
232 {
233         char challenge[8];
234
235         challenge_hash(peer_challenge, auth_challenge, user_name,
236                        challenge);
237
238         lrad_mschap(nt_password, challenge, response);
239 }
240
241 /*
242  *      auth_response() generates MS-CHAP v2 SUCCESS response
243  *      according to RFC 2759 GenerateAuthenticatorResponse()
244  *      returns 42-octet response string
245  */
246 static void auth_response(const char *username, const char *nt_password,
247                           char *ntresponse,
248                           char *peer_challenge, char *auth_challenge,
249                           char *response)
250 {
251         SHA1_CTX Context;
252         char hashhash[16];
253         const char magic1[39] =
254         {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
255          0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
256          0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
257          0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
258
259         const char magic2[41] =
260         {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
261          0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
262          0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
263          0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
264          0x6E};
265
266         char challenge[8];
267         char digest[20];
268
269         /*
270          * Hash password hash into hashhash
271          */
272
273         md4_calc(hashhash, nt_password, 16);
274
275         SHA1Init(&Context);
276         SHA1Update(&Context, hashhash, 16);
277         SHA1Update(&Context, ntresponse, 24);
278         SHA1Update(&Context, magic1, 39);
279         SHA1Final(digest, &Context);
280         challenge_hash(peer_challenge, auth_challenge, username, challenge);
281         SHA1Init(&Context);
282         SHA1Update(&Context, digest, 20);
283         SHA1Update(&Context, challenge, 8);
284         SHA1Update(&Context, magic2, 41);
285         SHA1Final(digest, &Context);
286
287         /*
288          * Encode the value of 'Digest' as "S=" followed by
289          * 40 ASCII hexadecimal digits and return it in
290          * AuthenticatorResponse.
291          * For example,
292          *   "S=0123456789ABCDEF0123456789ABCDEF01234567"
293          */
294         response[0] = 'S';
295         response[1] = '=';
296         bin2hex(digest, response + 2, 20);
297 }
298
299 struct mschap_instance {
300         int use_mppe;
301         int require_encryption;
302         int require_strong;
303         int with_ntdomain_hack;
304         char *passwd_file;
305         char *auth_type;
306 };
307
308 static CONF_PARSER module_config[] = {
309         /*
310          *      Cache the password by default.
311          */
312         { "use_mppe",    PW_TYPE_BOOLEAN,
313           offsetof(struct mschap_instance,use_mppe), NULL, "yes" },
314         { "require_encryption",    PW_TYPE_BOOLEAN,
315           offsetof(struct mschap_instance,require_encryption), NULL, "no" },
316         { "require_strong",    PW_TYPE_BOOLEAN,
317           offsetof(struct mschap_instance,require_strong), NULL, "no" },
318         { "with_ntdomain_hack",     PW_TYPE_BOOLEAN,
319           offsetof(struct mschap_instance,with_ntdomain_hack), NULL, "no" },
320         { "passwd",   PW_TYPE_STRING_PTR,
321           offsetof(struct mschap_instance, passwd_file), NULL,  NULL },
322         { "authtype",   PW_TYPE_STRING_PTR,
323           offsetof(struct mschap_instance, auth_type), NULL,  NULL },
324
325         { NULL, -1, 0, NULL, NULL }             /* end the list */
326 };
327
328 /*
329  *      deinstantiate module, free all memory allocated during
330  *      mschap_instantiate()
331  */
332 static int mschap_detach(void *instance){
333 #define inst ((struct mschap_instance *)instance)
334         if (inst->passwd_file) free(inst->passwd_file);
335         if (inst->auth_type) free(inst->auth_type);
336         free(instance);
337         return 0;
338 #undef inst
339 }
340
341 /*
342  *      Create instance for our module. Allocate space for
343  *      instance structure and read configuration parameters
344  */
345 static int mschap_instantiate(CONF_SECTION *conf, void **instance)
346 {
347         struct mschap_instance *inst;
348
349         inst = *instance = rad_malloc(sizeof(*inst));
350         if (!inst) {
351                 return -1;
352         }
353         memset(inst, 0, sizeof(*inst));
354
355         if (cf_section_parse(conf, inst, module_config) < 0) {
356                 free(inst);
357                 return -1;
358         }
359
360         /*
361          *      This module used to support SMB Password files, but it
362          *      made it too complicated.  If the user tries to
363          *      configure an SMB Password file, then die, with an
364          *      error message.
365          */
366         if (inst->passwd_file) {
367                 radlog(L_ERR, "rlm_mschap: SMB password file is no longer supported in this module.  Use rlm_passwd module instead");
368                 mschap_detach(inst);
369                 return -1;
370         }
371
372         return 0;
373 }
374
375 /*
376  *      add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
377  *      attribute to reply packet
378  */
379 static void add_reply(VALUE_PAIR** vp, unsigned char ident,
380                       const char* name, const char* value, int len)
381 {
382         VALUE_PAIR *reply_attr;
383         reply_attr = pairmake(name, "", T_OP_EQ);
384         if (!reply_attr) {
385                 DEBUG("  rlm_mschap: Failed to create attribute %s: %s\n", name, librad_errstr);
386                 return;
387         }
388
389         reply_attr->strvalue[0] = ident;
390         memcpy(reply_attr->strvalue + 1, value, len);
391         reply_attr->length = len + 1;
392         pairadd(vp, reply_attr);
393 }
394
395 /*
396  *      Add MPPE attributes to the reply.
397  */
398 static void mppe_add_reply(VALUE_PAIR** vp,
399                            const char* name, const char* value, int len)
400 {
401        VALUE_PAIR *reply_attr;
402        reply_attr = pairmake(name, "", T_OP_EQ);
403        if (!reply_attr) {
404                DEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, librad_errstr);
405                return;
406        }
407
408        memcpy(reply_attr->strvalue, value, len);
409        reply_attr->length = len;
410        pairadd(vp, reply_attr);
411 }
412
413 static const uint8_t SHSpad1[40] =
414                { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
416                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
417                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
418
419 static const uint8_t SHSpad2[40] =
420                { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
421                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
422                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
423                  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
424
425 static const uint8_t magic1[27] =
426                { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
427                  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
428                  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
429
430 static const uint8_t magic2[84] =
431                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
432                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
433                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
434                  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
435                  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
436                  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
437                  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
438                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
439                  0x6b, 0x65, 0x79, 0x2e };
440
441 static const uint8_t magic3[84] =
442                { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
443                  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
444                  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
445                  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
446                  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
447                  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
448                  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
449                  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
450                  0x6b, 0x65, 0x79, 0x2e };
451
452
453 static void mppe_GetMasterKey(uint8_t *nt_hashhash,uint8_t *nt_response,
454                               uint8_t *masterkey)
455 {
456        uint8_t digest[20];
457        SHA1_CTX Context;
458
459        SHA1Init(&Context);
460        SHA1Update(&Context,nt_hashhash,16);
461        SHA1Update(&Context,nt_response,24);
462        SHA1Update(&Context,magic1,27);
463        SHA1Final(digest,&Context);
464
465        memcpy(masterkey,digest,16);
466 }
467
468
469 static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
470                                        int keylen,int issend)
471 {
472        uint8_t digest[20];
473        const uint8_t *s;
474        SHA1_CTX Context;
475
476        memset(digest,0,20);
477
478        if(issend) {
479                s = magic3;
480        } else {
481                s = magic2;
482        }
483
484        SHA1Init(&Context);
485        SHA1Update(&Context,masterkey,16);
486        SHA1Update(&Context,SHSpad1,40);
487        SHA1Update(&Context,s,84);
488        SHA1Update(&Context,SHSpad2,40);
489        SHA1Final(digest,&Context);
490
491        memcpy(sesskey,digest,keylen);
492 }
493
494
495 static void mppe_chap2_get_keys128(uint8_t *nt_hashhash,uint8_t *nt_response,
496                                uint8_t *sendkey,uint8_t *recvkey)
497 {
498        uint8_t masterkey[16];
499
500        mppe_GetMasterKey(nt_hashhash,nt_response,masterkey);
501
502        mppe_GetAsymmetricStartKey(masterkey,sendkey,16,1);
503        mppe_GetAsymmetricStartKey(masterkey,recvkey,16,0);
504 }
505
506 /*
507  *      Generate MPPE keys.
508  */
509 static void mppe_chap2_gen_keys128(uint8_t *secret,uint8_t *vector,
510                                uint8_t *nt_hash,uint8_t *response,
511                                uint8_t *sendkey,uint8_t *recvkey)
512 {
513         uint8_t enckey1[16];
514         uint8_t enckey2[16];
515         /* uint8_t salt[2]; */
516         uint8_t nt_hashhash[16];
517
518         md4_calc(nt_hashhash,nt_hash,16);
519
520         mppe_chap2_get_keys128(nt_hashhash,response,enckey1,enckey2);
521
522         /*
523          *      dictionary.microsoft defines these attributes as
524          *      'encrypt=2'.  The functions in src/lib/radius.c will
525          *      take care of encrypting/decrypting them as appropriate,
526          *      so that we don't have to.
527          */
528         memcpy (sendkey, enckey1, 16);
529         memcpy (recvkey, enckey2, 16);
530 }
531
532
533 /*
534  *      mschap_authorize() - authorize user if we can authenticate
535  *      it later. Add Auth-Type attribute if present in module
536  *      configuration (usually Auth-Type must be "MS-CHAP")
537  */
538 static int mschap_authorize(void * instance, REQUEST *request)
539 {
540 #define inst ((struct mschap_instance *)instance)
541         VALUE_PAIR *challenge = NULL, *response = NULL;
542         VALUE_PAIR *vp;
543         const char *authtype_name = "MS-CHAP";
544
545         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
546         if (!challenge) {
547                 return RLM_MODULE_NOOP;
548         }
549
550         response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
551         if (!response)
552                 response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE);
553
554         /*
555          *      Nothing we recognize.  Don't do anything.
556          */
557         if (!response) {
558                 DEBUG2("  rlm_mschap: Found MS-CHAP-Challenge, but no MS-CHAP-Response.");
559                 return RLM_MODULE_NOOP;
560         }
561
562         /*
563          *      Choose MS-CHAP, or whatever else they told us to use.
564          */
565         if (inst->auth_type) {
566                 authtype_name = inst->auth_type;
567         }
568
569         DEBUG2("  rlm_mschap: Found MS-CHAP attributes.  Setting 'Auth-Type  = %s'", authtype_name);
570
571         /*
572          *      Set Auth-Type to MS-CHAP.  The authentication code
573          *      will take care of turning clear-text passwords into
574          *      NT/LM passwords.
575          */
576         pairdelete(&request->config_items, PW_AUTHTYPE);
577         vp = pairmake("Auth-Type", authtype_name, T_OP_EQ);
578         rad_assert(vp != NULL);
579         pairadd(&request->config_items, vp);
580
581         return RLM_MODULE_OK;
582 #undef inst
583 }
584
585 /*
586  *      mschap_authenticate() - authenticate user based on given
587  *      attributes and configuration.
588  *      We will try to find out password in configuration
589  *      or in configured passwd file.
590  *      If one is found we will check paraneters given by NAS.
591  *
592  *      If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have
593  *      one of:
594  *              PAP:      PW_PASSWORD or
595  *              MS-CHAP:  PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or
596  *              MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE
597  *      In case of password mismatch or locked account we MAY return
598  *      PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2
599  *      If MS-CHAP2 succeeds we MUST return
600  *      PW_MSCHAP2_SUCCESS
601  */
602 static int mschap_authenticate(void * instance, REQUEST *request)
603 {
604 #define inst ((struct mschap_instance *)instance)
605         VALUE_PAIR *challenge = NULL, *response = NULL;
606         VALUE_PAIR *password = NULL;
607         VALUE_PAIR *lm_password, *nt_password, *smb_ctrl;
608         VALUE_PAIR *username;
609         VALUE_PAIR *reply_attr;
610         uint8_t calculated[32];
611         uint8_t msch2resp[42];
612         uint8_t mppe_sendkey[34];
613         uint8_t mppe_recvkey[34];
614         char *username_string;
615         int chap = 0;
616
617         /*
618          *      Find the SMB-Account-Ctrl attribute, or the
619          *      SMB-Account-Ctrl-Text attribute.
620          */
621         smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL);
622         if (!smb_ctrl) {
623                 password = pairfind(request->config_items,
624                                     PW_SMB_ACCOUNT_CTRL_TEXT);
625                 if (password) {
626                         smb_ctrl = pairmake("SMB-Account-CTRL", "0", T_OP_SET);
627                         pairadd(&request->config_items, smb_ctrl);
628                         smb_ctrl->lvalue = pdb_decode_acct_ctrl(password->strvalue);
629                 }
630         }
631
632         /*
633          *      We're configured to do MS-CHAP authentication.
634          *      and account control information exists.  Enforce it.
635          */
636         if (smb_ctrl) {
637                 /*
638                  *      Password is not required.
639                  */
640                 if ((smb_ctrl->lvalue & ACB_PWNOTREQ) != 0) {
641                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says no password is required.");
642                         return RLM_MODULE_OK;
643                 }
644         }
645
646         /*
647          *      Decide how to get the passwords.
648          */
649         password = pairfind(request->config_items, PW_PASSWORD);
650
651         /*
652          *      We need an LM-Password.
653          */
654         lm_password = pairfind(request->config_items, PW_LM_PASSWORD);
655         if (lm_password) {
656                 /*
657                  *      Allow raw octets.
658                  */
659                 if ((lm_password->length == 16) ||
660                     ((lm_password->length == 32) &&
661                      (hex2bin(lm_password->strvalue,
662                               lm_password->strvalue, 16) == 16))) {
663                         DEBUG2("  rlm_mschap: Found LM-Password");
664                         lm_password->length = 16;
665
666                 } else {
667                         radlog(L_ERR, "rlm_mschap: Invalid LM-Password");
668                         lm_password = NULL;
669                 }
670
671         } else if (!password) {
672                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create LM-Password.");
673
674         } else {                /* there is a configured User-Password */
675                 lm_password = pairmake("LM-Password", "", T_OP_EQ);
676                 if (!lm_password) {
677                         radlog(L_ERR, "No memory");
678                 } else {
679                         lrad_lmpwdhash(password->strvalue,
680                                        lm_password->strvalue);
681                         lm_password->length = 16;
682                         pairadd(&request->config_items, lm_password);
683                 }
684         }
685
686         /*
687          *      We need an NT-Password.
688          */
689         nt_password = pairfind(request->config_items, PW_NT_PASSWORD);
690         if (nt_password) {
691                 if ((nt_password->length == 16) ||
692                     ((nt_password->length == 32) &&
693                      (hex2bin(nt_password->strvalue,
694                               nt_password->strvalue, 16) == 16))) {
695                         DEBUG2("  rlm_mschap: Found NT-Password");
696                         nt_password->length = 16;
697
698                 } else {
699                         radlog(L_ERR, "rlm_mschap: Invalid NT-Password");
700                         nt_password = NULL;
701                 }
702         } else if (!password) {
703                 DEBUG2("  rlm_mschap: No User-Password configured.  Cannot create NT-Password.");
704
705         } else {                /* there is a configured User-Password */
706                 nt_password = pairmake("NT-Password", "", T_OP_EQ);
707                 if (!nt_password) {
708                         radlog(L_ERR, "No memory");
709                 } else {
710                         ntpwdhash(nt_password->strvalue, password->strvalue);
711                         nt_password->length = 16;
712                         pairadd(&request->config_items, nt_password);
713                 }
714         }
715
716         /*
717          *      No NT or LM Passwords, die.
718          */
719         if (!lm_password && !nt_password) {
720                 DEBUG2("  rlm_mschap: No LM-Password or NT-Password attribute found.  Cannot perform MS-CHAP authentication.");
721                 return RLM_MODULE_FAIL;
722         }
723
724         /*
725          *      We MAY be asked to take a User-Password attribute from
726          *      the packet, and compare it to passwords retrieved from
727          *      an SMB Password file.
728          */
729         password = pairfind(request->packet->vps, PW_PASSWORD);
730         if (password) {
731                 if (lm_password) {
732                         lrad_lmpwdhash(calculated,
733                                        password->strvalue);
734                         if (memcmp(calculated,
735                                    lm_password->strvalue, 16) == 0) {
736                                 DEBUG2("  rlm_mschap: User-Password matches LM-Password.");
737                                 return RLM_MODULE_OK;
738                         } else {
739                                 DEBUG2("  rlm_mschap: FAILED: User-Password does NOT match LM-Password.");
740                         }
741                 } else if (nt_password) {
742                         ntpwdhash(calculated, password->strvalue);
743                         if (memcmp(calculated,
744                                    nt_password->strvalue, 16) == 0) {
745                                 DEBUG2("  rlm_mschap: User-Password matches NT-Password.");
746                                 return RLM_MODULE_OK;
747                         } else {
748                                 DEBUG2("  rlm_mschap: FAILED: User-Password does NOT match NT-Password.");
749                         }
750                 }
751                 return RLM_MODULE_REJECT;
752         } /* compare User-Password in packet to configured NT/LM-Password */
753
754         challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE);
755         if (!challenge) {
756                 DEBUG2("  rlm_mschap: No MS-CHAP-Challenge in the request");
757                 return RLM_MODULE_REJECT;
758         }
759
760         /*
761          *      We also require an MS-CHAP-Response.
762          */
763         response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE);
764         /*
765          *      MS-CHAP-Response, means MS-CHAPv1
766          */
767         if (response) {
768                 int offset;
769
770                 /*
771                  *      MS-CHAPv1 challenges are 8 octets.
772                  */
773                 if (challenge->length < 8) {
774                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
775                         return RLM_MODULE_INVALID;
776                 }
777
778                 /*
779                  *      Responses are 50 octets.
780                  */
781                 if (response->length < 50) {
782                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
783                         return RLM_MODULE_INVALID;
784                 }
785
786                 /*
787                  *      We are doing MS-CHAP.  Calculate the MS-CHAP
788                  *      response
789                  */
790                 if (response->strvalue[1] & 0x01) {
791                         DEBUG2("  rlm_mschap: doing MS-CHAPv1 with NT-Password");
792                         password = nt_password;
793                         offset = 26;
794                 } else {
795                         DEBUG2("  rlm_mschap: doing MS-CHAPv1 with LM-Password");
796                         password = lm_password;
797                         offset = 2;
798                 }
799
800                 /*
801                  *      No password configured.  Die.
802                  */
803                 if (!password) {
804                         DEBUG2("  rlm_mschap: FAILED: No NT/LM-Password");
805                         return RLM_MODULE_REJECT;
806                 }
807
808                 /*
809                  *      Calculate the expected response.
810                  */
811                 lrad_mschap(password->strvalue, challenge->strvalue,
812                             calculated);
813                 if (memcmp(response->strvalue + offset,
814                            calculated, 24) != 0) {
815                         DEBUG("  rlm_mschap: FAILED: MS-CHAP-Response is incorrect");
816                         return RLM_MODULE_FAIL;
817                 }
818
819                 chap = 1;
820
821         } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) {
822                 /*
823                  *      MS-CHAPv2 challenges are 16 octets.
824                  */
825                 if (challenge->length < 16) {
826                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Challenge has the wrong format.");
827                         return RLM_MODULE_INVALID;
828                 }
829
830                 /*
831                  *      Responses are 50 octets.
832                  */
833                 if (response->length < 50) {
834                         radlog(L_AUTH, "rlm_mschap: MS-CHAP-Response has the wrong format.");
835                         return RLM_MODULE_INVALID;
836                 }
837
838                 /*
839                  *      We also require a User-Name
840                  */
841                 username = pairfind(request->packet->vps, PW_USER_NAME);
842                 if (!username) {
843                         radlog(L_AUTH, "rlm_mschap: We require a User-Name for MS-CHAPv2");
844                         return RLM_MODULE_INVALID;
845                 }
846
847
848                 /*
849                  *with_ntdomain_hack moved here
850                  */
851                 if((username_string = strchr(username->strvalue, '\\')) != NULL) {
852                         if(inst->with_ntdomain_hack) {
853                                 username_string++;
854                         } else {
855                                 DEBUG2("  rlm_mschap: NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
856                                 username_string = username->strvalue;
857                         }
858                 } else {
859                         username_string = username->strvalue;
860                 }
861
862                 /*
863                  *      We are doing MS-CHAPv2
864                  *      We need NT hash for it to calculate response
865                  */
866                 if (!nt_password) {
867                         DEBUG2("  rlm_mschap: No NT-Password configured.  Cannot perform MS-CHAPv2 authentication.");
868                         return RLM_MODULE_INVALID;
869                 }
870
871                 DEBUG2("  rlm_mschap: doing MS-CHAPv2 with NT-Password");
872                 mschap2(response->strvalue + 2, challenge->strvalue,
873                         username_string, nt_password->strvalue,
874                         calculated);
875                 if (memcmp(response->strvalue + 26, calculated, 24) != 0) {
876                         DEBUG2("  rlm_mschap: FAILED: MS-CHAP2-Response is incorrect");
877                         add_reply(&request->reply->vps, *response->strvalue,
878                                   "MS-CHAP-Error", "E=691 R=1", 9);
879                         return RLM_MODULE_REJECT;
880                 }
881
882                 auth_response(username_string,
883                               nt_password->strvalue, calculated,
884                               response->strvalue + 2,
885                               challenge->strvalue,
886                               msch2resp);
887                 add_reply( &request->reply->vps, *response->strvalue,
888                            "MS-CHAP2-Success", msch2resp, 42);
889                 chap = 2;
890
891         } else {                /* Neither CHAPv1 or CHAPv2 response: die */
892                 radlog(L_AUTH, "rlm_mschap: No MS-CHAP response found");
893                 return RLM_MODULE_INVALID;
894         }
895
896         /*
897          *      We have a CHAP response, but the account may be
898          *      disabled.  Reject the user with the same error code
899          *      we use when their password is invalid.
900          */
901         if (smb_ctrl) {
902                 /*
903                  *      Account is disabled.
904                  *
905                  *      They're found, but they don't exist, so we
906                  *      return 'not found'.
907                  */
908                 if (((smb_ctrl->lvalue & ACB_DISABLED) != 0) ||
909                     ((smb_ctrl->lvalue & ACB_NORMAL) == 0)) {
910                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is disabled, or is not a normal account.");
911                         add_reply( &request->reply->vps, *response->strvalue,
912                                    "MS-CHAP-Error", "E=691 R=1", 9);
913                         return RLM_MODULE_NOTFOUND;
914                 }
915
916                 /*
917                  *      User is locked out.
918                  */
919                 if ((smb_ctrl->lvalue & ACB_AUTOLOCK) != 0) {
920                         DEBUG2("  rlm_mschap: SMB-Account-Ctrl says that the account is locked out.");
921                         add_reply( &request->reply->vps, *response->strvalue,
922                                    "MS-CHAP-Error", "E=647 R=0", 9);
923                         return RLM_MODULE_USERLOCK;
924                 }
925         }
926
927         /* now create MPPE attributes */
928         if (inst->use_mppe) {
929                 if (chap == 1){
930                         DEBUG2("rlm_mschap: adding MS-CHAPv1 MPPE keys");
931                         memset(mppe_sendkey, 0, 32);
932                         if (lm_password) {
933                                 memcpy(mppe_sendkey, lm_password->strvalue, 8);
934                         }
935
936                         if (nt_password) {
937                                 /*
938                                  *      According to RFC 2548 we
939                                  *      should send NT hash.  But in
940                                  *      practice it doesn't work.
941                                  *      Instead, we should send nthashhash
942                                  *
943                                  *      This is an error on RFC 2548.
944                                  */
945                                 md4_calc(mppe_sendkey + 8,
946                                          nt_password->strvalue, 16);
947                                 mppe_add_reply(&request->reply->vps,
948                                                "MS-CHAP-MPPE-Keys",
949                                                mppe_sendkey, 32);
950                         }
951                 } else if (chap == 2) {
952                         DEBUG2("rlm_mschap: adding MS-CHAPv2 MPPE keys");
953                         mppe_chap2_gen_keys128(request->secret,
954                                                request->packet->vector,
955                                                nt_password->strvalue,
956                                                response->strvalue + 26,
957                                                mppe_sendkey, mppe_recvkey);
958
959                         mppe_add_reply(&request->reply->vps,
960                                        "MS-MPPE-Recv-Key",
961                                        mppe_recvkey, 16);
962                         mppe_add_reply(&request->reply->vps,
963                                        "MS-MPPE-Send-Key",
964                                        mppe_sendkey, 16);
965
966                 }
967                 reply_attr = pairmake("MS-MPPE-Encryption-Policy",
968                                       (inst->require_encryption)? "0x00000002":"0x00000001",
969                                       T_OP_EQ);
970                 rad_assert(reply_attr != NULL);
971                 pairadd(&request->reply->vps, reply_attr);
972                 reply_attr = pairmake("MS-MPPE-Encryption-Types",
973                                       (inst->require_strong)? "0x00000004":"0x00000006",
974                                       T_OP_EQ);
975                 rad_assert(reply_attr != NULL);
976                 pairadd(&request->reply->vps, reply_attr);
977
978         } /* else we weren't asked to use MPPE */
979
980         return RLM_MODULE_OK;
981 #undef inst
982 }
983
984 module_t rlm_mschap = {
985   "MS-CHAP",
986   RLM_TYPE_THREAD_SAFE,         /* type */
987   NULL,                         /* initialize */
988   mschap_instantiate,           /* instantiation */
989   {
990           mschap_authenticate,  /* authenticate */
991           mschap_authorize,     /* authorize */
992           NULL,                 /* pre-accounting */
993           NULL,                 /* accounting */
994           NULL,                 /* checksimul */
995           NULL,                 /* pre-proxy */
996           NULL,                 /* post-proxy */
997           NULL                  /* post-auth */
998   },
999   mschap_detach,                /* detach */
1000   NULL,                         /* destroy */
1001 };