xref: /dragonfly/usr.sbin/ppp/chap_ms.c (revision dcb5d66b)
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 #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>
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
80 Get7Bits(u_char *input, int startBit)
81 {
82     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
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 */
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 */
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
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
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
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
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 #define LENGTH 20
191 static char *
192 SHA1_End(SHA_CTX *ctx, char *buf)
193 {
194     int i;
195     unsigned char digest[LENGTH];
196     static const char hex[]="0123456789abcdef";
197 
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];
206     }
207     buf[i+i] = '\0';
208     return buf;
209 }
210 
211 void
212 GenerateAuthenticatorResponse(char *Password, int PasswordLen,
213                               char *NTResponse, char *PeerChallenge,
214                               char *AuthenticatorChallenge, char *UserName,
215                               char *AuthenticatorResponse)
216 {
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;
223 
224       /*
225        * "Magic" constants used in response generation
226        */
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};
232 
233 
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};
240       /*
241        * Hash the password with MD4
242        */
243   NtPasswordHash(Password, PasswordLen, PasswordHash);
244       /*
245        * Now hash the hash
246        */
247   HashNtPasswordHash(PasswordHash, PasswordHashHash);
248 
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);
259 
260       /*
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"
266        */
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]);
272 
273 }
274 
275 void
276 GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey)
277 {
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};
284 
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);
291 }
292 
293 void
294 GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength,
295                      int IsSend, int IsServer)
296 {
297   char Digest[SHA_DIGEST_LENGTH];
298   SHA_CTX Context;
299   char *s;
300 
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};
311 
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};
322 
323   if (IsSend) {
324      if (IsServer) {
325         s = Magic3;
326      } else {
327         s = Magic2;
328      }
329   } else {
330      if (IsServer) {
331         s = Magic2;
332      } else {
333         s = Magic3;
334      }
335   }
336 
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);
343 
344   memcpy(SessionKey, Digest, SessionKeyLength);
345 }
346 
347 void
348 GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength,
349                  char *InterimKey)
350 {
351   SHA_CTX Context;
352   char Digest[SHA_DIGEST_LENGTH];
353 
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);
360 
361   memcpy(InterimKey, Digest, SessionKeyLength);
362 }
363 
364 #if 0
365 static void
366 Get_Key(char *InitialSessionKey, char *CurrentSessionKey,
367         int LengthOfDesiredKey)
368 {
369   SHA_CTX Context;
370   char Digest[SHA_DIGEST_LENGTH];
371 
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);
378 
379   memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey);
380 }
381 #endif
382 
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)
388 {
389     u_char response[24];
390 
391     ChallengeResponse(challenge, passwordHash, response);
392     memcpy(passwordHash, response, 24);
393     passwordHash[24] = 1;		/* NT-style response */
394 }
395 
396 void
397 mschap_LANMan(char *digest, char *challenge, char *secret)
398 {
399   static u_char salt[] = "KGS!@#$%";	/* RASAPI32.dll */
400   char SECRET[14], *ptr, *end;
401   u_char hash[16];
402 
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);
408 
409   DesEncrypt(salt, SECRET, hash);
410   DesEncrypt(salt, SECRET + 7, hash + 8);
411 
412   ChallengeResponse(challenge, hash, digest);
413 }
414