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