9490a5c6121169cd4eeb70af44076fada9b1d150
[freeradius.git] / src / modules / rlm_mschap / mschap.c
1 /*
2  * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2001,2006,2010  The FreeRADIUS server project
21  */
22
23
24 /*
25  *  This implements MS-CHAP, as described in RFC 2548
26  *
27  *  http://www.freeradius.org/rfc/rfc2548.txt
28  *
29  */
30
31 #include        <freeradius-devel/ident.h>
32 RCSID("$Id$")
33
34 #include        <freeradius-devel/radiusd.h>
35 #include        <freeradius-devel/modules.h>
36 #include        <freeradius-devel/rad_assert.h>
37 #include        <freeradius-devel/md5.h>
38 #include        <freeradius-devel/sha1.h>
39
40 #include        <ctype.h>
41
42 #include        "smbdes.h"
43
44 /*
45  *      ntpwdhash converts Unicode password to 16-byte NT hash
46  *      with MD4
47  */
48 void mschap_ntpwdhash (uint8_t *szHash, const char *szPassword)
49 {
50         char szUnicodePass[513];
51         int nPasswordLen;
52         int i;
53
54         /*
55          *      NT passwords are unicode.  Convert plain text password
56          *      to unicode by inserting a zero every other byte
57          */
58         nPasswordLen = strlen(szPassword);
59         for (i = 0; i < nPasswordLen; i++) {
60                 szUnicodePass[i << 1] = szPassword[i];
61                 szUnicodePass[(i << 1) + 1] = 0;
62         }
63
64         /* Encrypt Unicode password to a 16-byte MD4 hash */
65         fr_md4_calc(szHash, (uint8_t *) szUnicodePass, (nPasswordLen<<1) );
66 }
67
68
69 /*
70  *      challenge_hash() is used by mschap2() and auth_response()
71  *      implements RFC2759 ChallengeHash()
72  *      generates 64 bit challenge
73  */
74 void mschap_challenge_hash( const uint8_t *peer_challenge,
75                             const uint8_t *auth_challenge,
76                             const char *user_name, uint8_t *challenge )
77 {
78         fr_SHA1_CTX Context;
79         uint8_t hash[20];
80
81         fr_SHA1Init(&Context);
82         fr_SHA1Update(&Context, peer_challenge, 16);
83         fr_SHA1Update(&Context, auth_challenge, 16);
84         fr_SHA1Update(&Context, (const uint8_t *) user_name,
85                       strlen(user_name));
86         fr_SHA1Final(hash, &Context);
87         memcpy(challenge, hash, 8);
88 }
89
90 /*
91  *      auth_response() generates MS-CHAP v2 SUCCESS response
92  *      according to RFC 2759 GenerateAuthenticatorResponse()
93  *      returns 42-octet response string
94  */
95 void mschap_auth_response(const char *username,
96                           const uint8_t *nt_hash_hash,
97                           uint8_t *ntresponse,
98                           uint8_t *peer_challenge, uint8_t *auth_challenge,
99                           char *response)
100 {
101         fr_SHA1_CTX Context;
102         static const uint8_t magic1[39] =
103         {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
104          0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
105          0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
106          0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
107
108         static const uint8_t magic2[41] =
109         {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
110          0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
111          0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
112          0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
113          0x6E};
114
115         static const char hex[16] = "0123456789ABCDEF";
116
117         size_t i;
118         uint8_t challenge[8];
119         uint8_t digest[20];
120
121         fr_SHA1Init(&Context);
122         fr_SHA1Update(&Context, nt_hash_hash, 16);
123         fr_SHA1Update(&Context, ntresponse, 24);
124         fr_SHA1Update(&Context, magic1, 39);
125         fr_SHA1Final(digest, &Context);
126         mschap_challenge_hash(peer_challenge, auth_challenge, username, challenge);
127         fr_SHA1Init(&Context);
128         fr_SHA1Update(&Context, digest, 20);
129         fr_SHA1Update(&Context, challenge, 8);
130         fr_SHA1Update(&Context, magic2, 41);
131         fr_SHA1Final(digest, &Context);
132
133         /*
134          *      Encode the value of 'Digest' as "S=" followed by
135          *      40 ASCII hexadecimal digits and return it in
136          *      AuthenticatorResponse.
137          *      For example,
138          *      "S=0123456789ABCDEF0123456789ABCDEF01234567"
139          */
140         response[0] = 'S';
141         response[1] = '=';
142
143         /*
144          *      The hexadecimal digits [A-F] MUST be uppercase.
145          */
146         for (i = 0; i < sizeof(digest); i++) {
147                 response[2 + (i * 2)] = hex[(digest[i] >> 4) & 0x0f];
148                 response[3 + (i * 2)] = hex[digest[i] & 0x0f];
149         }
150 }
151