1 /*-
2 * Copyright (c) 1997 Gabor Kincses <gabor@acm.org>
3 * 1997 - 2001 Brian Somers <brian@Awfulhak.org>
4 * based on work by Eric Rosenquist
5 * Strata Software Limited.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: src/usr.sbin/ppp/chap_ms.c,v 1.9.2.6 2002/09/01 02:12:23 brian Exp $
30 */
31
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #if defined(__DragonFly__) || defined(__NetBSD__)
36 #include <openssl/des.h>
37 #else
38 #include <des.h>
39 #endif
40 #include <openssl/sha.h>
41 #include <openssl/md4.h>
42 #include <string.h>
43
44 #include "chap_ms.h"
45
46 /*
47 * Documentation & specifications:
48 *
49 * MS-CHAP (CHAP80) rfc2433
50 * MS-CHAP-V2 (CHAP81) rfc2759
51 * MPPE key management draft-ietf-pppext-mppe-keys-02.txt
52 */
53
54 static char SHA1_Pad1[40] =
55 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
59
60 static char SHA1_Pad2[40] =
61 {0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
62 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
63 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
64 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2};
65
66 /* unused, for documentation only */
67 /* only NTResp is filled in for FreeBSD */
68 struct MS_ChapResponse {
69 u_char LANManResp[24];
70 u_char NTResp[24];
71 u_char UseNT; /* If 1, ignore the LANMan response field */
72 };
73
74 static u_char
Get7Bits(u_char * input,int startBit)75 Get7Bits(u_char *input, int startBit)
76 {
77 unsigned int word;
78
79 word = (unsigned)input[startBit / 8] << 8;
80 word |= (unsigned)input[startBit / 8 + 1];
81
82 word >>= 15 - (startBit % 8 + 7);
83
84 return word & 0xFE;
85 }
86
87 /* IN 56 bit DES key missing parity bits
88 OUT 64 bit DES key with parity bits added */
89 static void
MakeKey(u_char * key,u_char * des_key)90 MakeKey(u_char *key, u_char *des_key)
91 {
92 des_key[0] = Get7Bits(key, 0);
93 des_key[1] = Get7Bits(key, 7);
94 des_key[2] = Get7Bits(key, 14);
95 des_key[3] = Get7Bits(key, 21);
96 des_key[4] = Get7Bits(key, 28);
97 des_key[5] = Get7Bits(key, 35);
98 des_key[6] = Get7Bits(key, 42);
99 des_key[7] = Get7Bits(key, 49);
100
101 DES_set_odd_parity((DES_cblock *)des_key);
102 }
103
104 static void /* IN 8 octets IN 7 octest OUT 8 octets */
DesEncrypt(u_char * clear,u_char * key,u_char * cipher)105 DesEncrypt(u_char *clear, u_char *key, u_char *cipher)
106 {
107 DES_cblock des_key;
108 DES_key_schedule key_schedule;
109
110 MakeKey(key, des_key);
111 DES_set_key(&des_key, &key_schedule);
112 DES_ecb_encrypt((DES_cblock *)clear, (DES_cblock *)cipher, &key_schedule, 1);
113 }
114
115 static void /* IN 8 octets IN 16 octets OUT 24 octets */
ChallengeResponse(u_char * challenge,u_char * pwHash,u_char * response)116 ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response)
117 {
118 char ZPasswordHash[21];
119
120 memset(ZPasswordHash, '\0', sizeof ZPasswordHash);
121 memcpy(ZPasswordHash, pwHash, 16);
122
123 DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
124 DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
125 DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
126 }
127
128 void
NtPasswordHash(char * key,int keylen,char * hash)129 NtPasswordHash(char *key, int keylen, char *hash)
130 {
131 MD4_CTX MD4context;
132
133 MD4_Init(&MD4context);
134 MD4_Update(&MD4context, key, keylen);
135 MD4_Final(hash, &MD4context);
136 }
137
138 void
HashNtPasswordHash(char * hash,char * hashhash)139 HashNtPasswordHash(char *hash, char *hashhash)
140 {
141 MD4_CTX MD4context;
142
143 MD4_Init(&MD4context);
144 MD4_Update(&MD4context, hash, 16);
145 MD4_Final(hashhash, &MD4context);
146 }
147
148 static void
ChallengeHash(char * PeerChallenge,char * AuthenticatorChallenge,char * UserName,char * Challenge)149 ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge,
150 char *UserName, char *Challenge)
151 {
152 SHA_CTX Context;
153 char Digest[SHA_DIGEST_LENGTH];
154 char *Name;
155
156 Name = strrchr(UserName, '\\');
157 if(NULL == Name)
158 Name = UserName;
159 else
160 Name++;
161
162 SHA1_Init(&Context);
163
164 SHA1_Update(&Context, PeerChallenge, 16);
165 SHA1_Update(&Context, AuthenticatorChallenge, 16);
166 SHA1_Update(&Context, Name, strlen(Name));
167
168 SHA1_Final(Digest, &Context);
169 memcpy(Challenge, Digest, 8);
170 }
171
172 void
GenerateNTResponse(char * AuthenticatorChallenge,char * PeerChallenge,char * UserName,char * Password,int PasswordLen,char * Response)173 GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge,
174 char *UserName, char *Password,
175 int PasswordLen, char *Response)
176 {
177 char Challenge[8];
178 char PasswordHash[16];
179
180 ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
181 NtPasswordHash(Password, PasswordLen, PasswordHash);
182 ChallengeResponse(Challenge, PasswordHash, Response);
183 }
184
185 #define LENGTH 20
186 static char *
SHA1_End(SHA_CTX * ctx,char * buf)187 SHA1_End(SHA_CTX *ctx, char *buf)
188 {
189 int i;
190 unsigned char digest[LENGTH];
191 static const char hex[]="0123456789abcdef";
192
193 if (!buf)
194 buf = malloc(2*LENGTH + 1);
195 if (!buf)
196 return 0;
197 SHA1_Final(digest, ctx);
198 for (i = 0; i < LENGTH; i++) {
199 buf[i+i] = hex[digest[i] >> 4];
200 buf[i+i+1] = hex[digest[i] & 0x0f];
201 }
202 buf[i+i] = '\0';
203 return buf;
204 }
205
206 void
GenerateAuthenticatorResponse(char * Password,int PasswordLen,char * NTResponse,char * PeerChallenge,char * AuthenticatorChallenge,char * UserName,char * AuthenticatorResponse)207 GenerateAuthenticatorResponse(char *Password, int PasswordLen,
208 char *NTResponse, char *PeerChallenge,
209 char *AuthenticatorChallenge, char *UserName,
210 char *AuthenticatorResponse)
211 {
212 SHA_CTX Context;
213 char PasswordHash[16];
214 char PasswordHashHash[16];
215 char Challenge[8];
216 u_char Digest[SHA_DIGEST_LENGTH];
217 int i;
218
219 /*
220 * "Magic" constants used in response generation
221 */
222 char Magic1[39] =
223 {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
224 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
225 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
226 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
227
228
229 char Magic2[41] =
230 {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
231 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
232 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
233 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
234 0x6E};
235 /*
236 * Hash the password with MD4
237 */
238 NtPasswordHash(Password, PasswordLen, PasswordHash);
239 /*
240 * Now hash the hash
241 */
242 HashNtPasswordHash(PasswordHash, PasswordHashHash);
243
244 SHA1_Init(&Context);
245 SHA1_Update(&Context, PasswordHashHash, 16);
246 SHA1_Update(&Context, NTResponse, 24);
247 SHA1_Update(&Context, Magic1, 39);
248 SHA1_Final(Digest, &Context);
249 ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
250 SHA1_Init(&Context);
251 SHA1_Update(&Context, Digest, 20);
252 SHA1_Update(&Context, Challenge, 8);
253 SHA1_Update(&Context, Magic2, 41);
254
255 /*
256 * Encode the value of 'Digest' as "S=" followed by
257 * 40 ASCII hexadecimal digits and return it in
258 * AuthenticatorResponse.
259 * For example,
260 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
261 */
262 AuthenticatorResponse[0] = 'S';
263 AuthenticatorResponse[1] = '=';
264 SHA1_End(&Context, AuthenticatorResponse + 2);
265 for (i=2; i<42; i++)
266 AuthenticatorResponse[i] = toupper(AuthenticatorResponse[i]);
267
268 }
269
270 void
GetMasterKey(char * PasswordHashHash,char * NTResponse,char * MasterKey)271 GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey)
272 {
273 char Digest[SHA_DIGEST_LENGTH];
274 SHA_CTX Context;
275 static char Magic1[27] =
276 {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
277 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
278 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};
279
280 SHA1_Init(&Context);
281 SHA1_Update(&Context, PasswordHashHash, 16);
282 SHA1_Update(&Context, NTResponse, 24);
283 SHA1_Update(&Context, Magic1, 27);
284 SHA1_Final(Digest, &Context);
285 memcpy(MasterKey, Digest, 16);
286 }
287
288 void
GetAsymetricStartKey(char * MasterKey,char * SessionKey,int SessionKeyLength,int IsSend,int IsServer)289 GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength,
290 int IsSend, int IsServer)
291 {
292 char Digest[SHA_DIGEST_LENGTH];
293 SHA_CTX Context;
294 char *s;
295
296 static char Magic2[84] =
297 {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
298 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
299 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
300 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
301 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
302 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
303 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
304 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
305 0x6b, 0x65, 0x79, 0x2e};
306
307 static char Magic3[84] =
308 {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
309 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
310 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
311 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
312 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
313 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
314 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
315 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
316 0x6b, 0x65, 0x79, 0x2e};
317
318 if (IsSend) {
319 if (IsServer) {
320 s = Magic3;
321 } else {
322 s = Magic2;
323 }
324 } else {
325 if (IsServer) {
326 s = Magic2;
327 } else {
328 s = Magic3;
329 }
330 }
331
332 SHA1_Init(&Context);
333 SHA1_Update(&Context, MasterKey, 16);
334 SHA1_Update(&Context, SHA1_Pad1, 40);
335 SHA1_Update(&Context, s, 84);
336 SHA1_Update(&Context, SHA1_Pad2, 40);
337 SHA1_Final(Digest, &Context);
338
339 memcpy(SessionKey, Digest, SessionKeyLength);
340 }
341
342 void
GetNewKeyFromSHA(char * StartKey,char * SessionKey,long SessionKeyLength,char * InterimKey)343 GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength,
344 char *InterimKey)
345 {
346 SHA_CTX Context;
347 char Digest[SHA_DIGEST_LENGTH];
348
349 SHA1_Init(&Context);
350 SHA1_Update(&Context, StartKey, SessionKeyLength);
351 SHA1_Update(&Context, SHA1_Pad1, 40);
352 SHA1_Update(&Context, SessionKey, SessionKeyLength);
353 SHA1_Update(&Context, SHA1_Pad2, 40);
354 SHA1_Final(Digest, &Context);
355
356 memcpy(InterimKey, Digest, SessionKeyLength);
357 }
358
359 #if 0
360 static void
361 Get_Key(char *InitialSessionKey, char *CurrentSessionKey,
362 int LengthOfDesiredKey)
363 {
364 SHA_CTX Context;
365 char Digest[SHA_DIGEST_LENGTH];
366
367 SHA1_Init(&Context);
368 SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey);
369 SHA1_Update(&Context, SHA1_Pad1, 40);
370 SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey);
371 SHA1_Update(&Context, SHA1_Pad2, 40);
372 SHA1_Final(Digest, &Context);
373
374 memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey);
375 }
376 #endif
377
378 /* passwordHash 16-bytes MD4 hashed password
379 challenge 8-bytes peer CHAP challenge
380 since passwordHash is in a 24-byte buffer, response is written in there */
381 void
mschap_NT(char * passwordHash,char * challenge)382 mschap_NT(char *passwordHash, char *challenge)
383 {
384 u_char response[24];
385
386 ChallengeResponse(challenge, passwordHash, response);
387 memcpy(passwordHash, response, 24);
388 passwordHash[24] = 1; /* NT-style response */
389 }
390
391 void
mschap_LANMan(char * digest,char * challenge,char * secret)392 mschap_LANMan(char *digest, char *challenge, char *secret)
393 {
394 static u_char salt[] = "KGS!@#$%"; /* RASAPI32.dll */
395 char SECRET[14], *ptr, *end;
396 u_char hash[16];
397
398 end = SECRET + sizeof SECRET;
399 for (ptr = SECRET; *secret && ptr < end; ptr++, secret++)
400 *ptr = toupper(*secret);
401 if (ptr < end)
402 memset(ptr, '\0', end - ptr);
403
404 DesEncrypt(salt, SECRET, hash);
405 DesEncrypt(salt, SECRET + 7, hash + 8);
406
407 ChallengeResponse(challenge, hash, digest);
408 }
409