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.
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.
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
20 * Copyright 2000,2001,2006,2010 The FreeRADIUS server project
25 * This implements MS-CHAP, as described in RFC 2548
27 * http://www.freeradius.org/rfc/rfc2548.txt
31 #include <freeradius-devel/ident.h>
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>
46 * ntpwdhash converts Unicode password to 16-byte NT hash
49 void mschap_ntpwdhash (uint8_t *szHash, const char *szPassword)
51 char szUnicodePass[513];
56 * NT passwords are unicode. Convert plain text password
57 * to unicode by inserting a zero every other byte
59 nPasswordLen = strlen(szPassword);
60 for (i = 0; i < nPasswordLen; i++) {
61 szUnicodePass[i << 1] = szPassword[i];
62 szUnicodePass[(i << 1) + 1] = 0;
65 /* Encrypt Unicode password to a 16-byte MD4 hash */
66 fr_md4_calc(szHash, (uint8_t *) szUnicodePass, (nPasswordLen<<1) );
71 * challenge_hash() is used by mschap2() and auth_response()
72 * implements RFC2759 ChallengeHash()
73 * generates 64 bit challenge
75 void mschap_challenge_hash( const uint8_t *peer_challenge,
76 const uint8_t *auth_challenge,
77 const char *user_name, uint8_t *challenge )
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,
87 fr_SHA1Final(hash, &Context);
88 memcpy(challenge, hash, 8);
92 * auth_response() generates MS-CHAP v2 SUCCESS response
93 * according to RFC 2759 GenerateAuthenticatorResponse()
94 * returns 42-octet response string
96 void mschap_auth_response(const char *username,
97 const uint8_t *nt_hash_hash,
99 uint8_t *peer_challenge, uint8_t *auth_challenge,
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};
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,
116 static const char hex[16] = "0123456789ABCDEF";
119 uint8_t challenge[8];
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);
135 * Encode the value of 'Digest' as "S=" followed by
136 * 40 ASCII hexadecimal digits and return it in
137 * AuthenticatorResponse.
139 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
145 * The hexadecimal digits [A-F] MUST be uppercase.
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];