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