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>
45 * ntpwdhash converts Unicode password to 16-byte NT hash
48 void mschap_ntpwdhash (uint8_t *szHash, const char *szPassword)
50 char szUnicodePass[513];
55 * NT passwords are unicode. Convert plain text password
56 * to unicode by inserting a zero every other byte
58 nPasswordLen = strlen(szPassword);
59 for (i = 0; i < nPasswordLen; i++) {
60 szUnicodePass[i << 1] = szPassword[i];
61 szUnicodePass[(i << 1) + 1] = 0;
64 /* Encrypt Unicode password to a 16-byte MD4 hash */
65 fr_md4_calc(szHash, (uint8_t *) szUnicodePass, (nPasswordLen<<1) );
70 * challenge_hash() is used by mschap2() and auth_response()
71 * implements RFC2759 ChallengeHash()
72 * generates 64 bit challenge
74 void mschap_challenge_hash( const uint8_t *peer_challenge,
75 const uint8_t *auth_challenge,
76 const char *user_name, uint8_t *challenge )
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,
86 fr_SHA1Final(hash, &Context);
87 memcpy(challenge, hash, 8);
91 * auth_response() generates MS-CHAP v2 SUCCESS response
92 * according to RFC 2759 GenerateAuthenticatorResponse()
93 * returns 42-octet response string
95 void mschap_auth_response(const char *username,
96 const uint8_t *nt_hash_hash,
98 uint8_t *peer_challenge, uint8_t *auth_challenge,
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};
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,
115 static const char hex[16] = "0123456789ABCDEF";
118 uint8_t challenge[8];
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);
134 * Encode the value of 'Digest' as "S=" followed by
135 * 40 ASCII hexadecimal digits and return it in
136 * AuthenticatorResponse.
138 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
144 * The hexadecimal digits [A-F] MUST be uppercase.
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];