hack(6): Separate hostprog.
[dragonfly.git] / usr.sbin / ppp / chap_ms.c
blob33729d2226acd23f28c6e920baa196c6282a81fb
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.
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.
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.
29 * $FreeBSD: src/usr.sbin/ppp/chap_ms.c,v 1.9.2.6 2002/09/01 02:12:23 brian Exp $
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #ifdef __DragonFly__
36 #include <openssl/des.h>
37 #include <sha.h>
38 #else
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>
49 #include "chap_ms.h"
52 * Documentation & specifications:
54 * MS-CHAP (CHAP80) rfc2433
55 * MS-CHAP-V2 (CHAP81) rfc2759
56 * MPPE key management draft-ietf-pppext-mppe-keys-02.txt
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};
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};
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 */
79 static u_char
80 Get7Bits(u_char *input, int startBit)
82 unsigned int word;
84 word = (unsigned)input[startBit / 8] << 8;
85 word |= (unsigned)input[startBit / 8 + 1];
87 word >>= 15 - (startBit % 8 + 7);
89 return word & 0xFE;
92 /* IN 56 bit DES key missing parity bits
93 OUT 64 bit DES key with parity bits added */
94 static void
95 MakeKey(u_char *key, u_char *des_key)
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);
106 DES_set_odd_parity((DES_cblock *)des_key);
109 static void /* IN 8 octets IN 7 octest OUT 8 octets */
110 DesEncrypt(u_char *clear, u_char *key, u_char *cipher)
112 DES_cblock des_key;
113 DES_key_schedule key_schedule;
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);
120 static void /* IN 8 octets IN 16 octets OUT 24 octets */
121 ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response)
123 char ZPasswordHash[21];
125 memset(ZPasswordHash, '\0', sizeof ZPasswordHash);
126 memcpy(ZPasswordHash, pwHash, 16);
128 DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
129 DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
130 DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
133 void
134 NtPasswordHash(char *key, int keylen, char *hash)
136 MD4_CTX MD4context;
138 MD4Init(&MD4context);
139 MD4Update(&MD4context, key, keylen);
140 MD4Final(hash, &MD4context);
143 void
144 HashNtPasswordHash(char *hash, char *hashhash)
146 MD4_CTX MD4context;
148 MD4Init(&MD4context);
149 MD4Update(&MD4context, hash, 16);
150 MD4Final(hashhash, &MD4context);
153 static void
154 ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge,
155 char *UserName, char *Challenge)
157 SHA_CTX Context;
158 char Digest[SHA_DIGEST_LENGTH];
159 char *Name;
161 Name = strrchr(UserName, '\\');
162 if(NULL == Name)
163 Name = UserName;
164 else
165 Name++;
167 SHA1_Init(&Context);
169 SHA1_Update(&Context, PeerChallenge, 16);
170 SHA1_Update(&Context, AuthenticatorChallenge, 16);
171 SHA1_Update(&Context, Name, strlen(Name));
173 SHA1_Final(Digest, &Context);
174 memcpy(Challenge, Digest, 8);
177 void
178 GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge,
179 char *UserName, char *Password,
180 int PasswordLen, char *Response)
182 char Challenge[8];
183 char PasswordHash[16];
185 ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
186 NtPasswordHash(Password, PasswordLen, PasswordHash);
187 ChallengeResponse(Challenge, PasswordHash, Response);
190 #define LENGTH 20
191 static char *
192 SHA1_End(SHA_CTX *ctx, char *buf)
194 int i;
195 unsigned char digest[LENGTH];
196 static const char hex[]="0123456789abcdef";
198 if (!buf)
199 buf = malloc(2*LENGTH + 1);
200 if (!buf)
201 return 0;
202 SHA1_Final(digest, ctx);
203 for (i = 0; i < LENGTH; i++) {
204 buf[i+i] = hex[digest[i] >> 4];
205 buf[i+i+1] = hex[digest[i] & 0x0f];
207 buf[i+i] = '\0';
208 return buf;
211 void
212 GenerateAuthenticatorResponse(char *Password, int PasswordLen,
213 char *NTResponse, char *PeerChallenge,
214 char *AuthenticatorChallenge, char *UserName,
215 char *AuthenticatorResponse)
217 SHA_CTX Context;
218 char PasswordHash[16];
219 char PasswordHashHash[16];
220 char Challenge[8];
221 u_char Digest[SHA_DIGEST_LENGTH];
222 int i;
225 * "Magic" constants used in response generation
227 char Magic1[39] =
228 {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
229 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
230 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
231 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
234 char Magic2[41] =
235 {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
236 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
237 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
238 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
239 0x6E};
241 * Hash the password with MD4
243 NtPasswordHash(Password, PasswordLen, PasswordHash);
245 * Now hash the hash
247 HashNtPasswordHash(PasswordHash, PasswordHashHash);
249 SHA1_Init(&Context);
250 SHA1_Update(&Context, PasswordHashHash, 16);
251 SHA1_Update(&Context, NTResponse, 24);
252 SHA1_Update(&Context, Magic1, 39);
253 SHA1_Final(Digest, &Context);
254 ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
255 SHA1_Init(&Context);
256 SHA1_Update(&Context, Digest, 20);
257 SHA1_Update(&Context, Challenge, 8);
258 SHA1_Update(&Context, Magic2, 41);
261 * Encode the value of 'Digest' as "S=" followed by
262 * 40 ASCII hexadecimal digits and return it in
263 * AuthenticatorResponse.
264 * For example,
265 * "S=0123456789ABCDEF0123456789ABCDEF01234567"
267 AuthenticatorResponse[0] = 'S';
268 AuthenticatorResponse[1] = '=';
269 SHA1_End(&Context, AuthenticatorResponse + 2);
270 for (i=2; i<42; i++)
271 AuthenticatorResponse[i] = toupper(AuthenticatorResponse[i]);
275 void
276 GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey)
278 char Digest[SHA_DIGEST_LENGTH];
279 SHA_CTX Context;
280 static char Magic1[27] =
281 {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
282 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
283 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};
285 SHA1_Init(&Context);
286 SHA1_Update(&Context, PasswordHashHash, 16);
287 SHA1_Update(&Context, NTResponse, 24);
288 SHA1_Update(&Context, Magic1, 27);
289 SHA1_Final(Digest, &Context);
290 memcpy(MasterKey, Digest, 16);
293 void
294 GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength,
295 int IsSend, int IsServer)
297 char Digest[SHA_DIGEST_LENGTH];
298 SHA_CTX Context;
299 char *s;
301 static char Magic2[84] =
302 {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
303 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
304 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
305 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
306 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
307 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
308 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
309 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
310 0x6b, 0x65, 0x79, 0x2e};
312 static char Magic3[84] =
313 {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
314 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
315 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
316 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
317 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
318 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
319 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
320 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
321 0x6b, 0x65, 0x79, 0x2e};
323 if (IsSend) {
324 if (IsServer) {
325 s = Magic3;
326 } else {
327 s = Magic2;
329 } else {
330 if (IsServer) {
331 s = Magic2;
332 } else {
333 s = Magic3;
337 SHA1_Init(&Context);
338 SHA1_Update(&Context, MasterKey, 16);
339 SHA1_Update(&Context, SHA1_Pad1, 40);
340 SHA1_Update(&Context, s, 84);
341 SHA1_Update(&Context, SHA1_Pad2, 40);
342 SHA1_Final(Digest, &Context);
344 memcpy(SessionKey, Digest, SessionKeyLength);
347 void
348 GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength,
349 char *InterimKey)
351 SHA_CTX Context;
352 char Digest[SHA_DIGEST_LENGTH];
354 SHA1_Init(&Context);
355 SHA1_Update(&Context, StartKey, SessionKeyLength);
356 SHA1_Update(&Context, SHA1_Pad1, 40);
357 SHA1_Update(&Context, SessionKey, SessionKeyLength);
358 SHA1_Update(&Context, SHA1_Pad2, 40);
359 SHA1_Final(Digest, &Context);
361 memcpy(InterimKey, Digest, SessionKeyLength);
364 #if 0
365 static void
366 Get_Key(char *InitialSessionKey, char *CurrentSessionKey,
367 int LengthOfDesiredKey)
369 SHA_CTX Context;
370 char Digest[SHA_DIGEST_LENGTH];
372 SHA1_Init(&Context);
373 SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey);
374 SHA1_Update(&Context, SHA1_Pad1, 40);
375 SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey);
376 SHA1_Update(&Context, SHA1_Pad2, 40);
377 SHA1_Final(Digest, &Context);
379 memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey);
381 #endif
383 /* passwordHash 16-bytes MD4 hashed password
384 challenge 8-bytes peer CHAP challenge
385 since passwordHash is in a 24-byte buffer, response is written in there */
386 void
387 mschap_NT(char *passwordHash, char *challenge)
389 u_char response[24];
391 ChallengeResponse(challenge, passwordHash, response);
392 memcpy(passwordHash, response, 24);
393 passwordHash[24] = 1; /* NT-style response */
396 void
397 mschap_LANMan(char *digest, char *challenge, char *secret)
399 static u_char salt[] = "KGS!@#$%"; /* RASAPI32.dll */
400 char SECRET[14], *ptr, *end;
401 u_char hash[16];
403 end = SECRET + sizeof SECRET;
404 for (ptr = SECRET; *secret && ptr < end; ptr++, secret++)
405 *ptr = toupper(*secret);
406 if (ptr < end)
407 memset(ptr, '\0', end - ptr);
409 DesEncrypt(salt, SECRET, hash);
410 DesEncrypt(salt, SECRET + 7, hash + 8);
412 ChallengeResponse(challenge, hash, digest);