xref: /dragonfly/usr.sbin/ppp/chap_ms.c (revision 65cc0652)
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 #ifdef __DragonFly__
34 #include <sys/types.h>
35 #include <openssl/des.h>
36 #include <sha.h>
37 #include <stdlib.h>
38 #else
39 #include <sys/types.h>
40 #include <stdlib.h>
41 #ifdef __NetBSD__
42 #include <openssl/des.h>
43 #else
44 #include <des.h>
45 #endif
46 #include <openssl/sha.h>
47 #endif
48 #include <md4.h>
49 #include <string.h>
50 
51 #include "chap_ms.h"
52 
53 /*
54  * Documentation & specifications:
55  *
56  * MS-CHAP (CHAP80)	rfc2433
57  * MS-CHAP-V2 (CHAP81)	rfc2759
58  * MPPE key management	draft-ietf-pppext-mppe-keys-02.txt
59  */
60 
61 static char SHA1_Pad1[40] =
62   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
66 
67 static char SHA1_Pad2[40] =
68   {0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
69    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
70    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
71    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2};
72 
73 /* unused, for documentation only */
74 /* only NTResp is filled in for FreeBSD */
75 struct MS_ChapResponse {
76     u_char LANManResp[24];
77     u_char NTResp[24];
78     u_char UseNT;	/* If 1, ignore the LANMan response field */
79 };
80 
81 static u_char
82 Get7Bits(u_char *input, int startBit)
83 {
84     unsigned int word;
85 
86     word  = (unsigned)input[startBit / 8] << 8;
87     word |= (unsigned)input[startBit / 8 + 1];
88 
89     word >>= 15 - (startBit % 8 + 7);
90 
91     return word & 0xFE;
92 }
93 
94 /* IN  56 bit DES key missing parity bits
95    OUT 64 bit DES key with parity bits added */
96 static void
97 MakeKey(u_char *key, u_char *des_key)
98 {
99     des_key[0] = Get7Bits(key,  0);
100     des_key[1] = Get7Bits(key,  7);
101     des_key[2] = Get7Bits(key, 14);
102     des_key[3] = Get7Bits(key, 21);
103     des_key[4] = Get7Bits(key, 28);
104     des_key[5] = Get7Bits(key, 35);
105     des_key[6] = Get7Bits(key, 42);
106     des_key[7] = Get7Bits(key, 49);
107 
108     DES_set_odd_parity((DES_cblock *)des_key);
109 }
110 
111 static void /* IN 8 octets IN 7 octest OUT 8 octets */
112 DesEncrypt(u_char *clear, u_char *key, u_char *cipher)
113 {
114     DES_cblock		des_key;
115     DES_key_schedule	key_schedule;
116 
117     MakeKey(key, des_key);
118     DES_set_key(&des_key, &key_schedule);
119     DES_ecb_encrypt((DES_cblock *)clear, (DES_cblock *)cipher, &key_schedule, 1);
120 }
121 
122 static void      /* IN 8 octets      IN 16 octets     OUT 24 octets */
123 ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response)
124 {
125     char    ZPasswordHash[21];
126 
127     memset(ZPasswordHash, '\0', sizeof ZPasswordHash);
128     memcpy(ZPasswordHash, pwHash, 16);
129 
130     DesEncrypt(challenge, ZPasswordHash +  0, response + 0);
131     DesEncrypt(challenge, ZPasswordHash +  7, response + 8);
132     DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
133 }
134 
135 void
136 NtPasswordHash(char *key, int keylen, char *hash)
137 {
138   MD4_CTX MD4context;
139 
140   MD4Init(&MD4context);
141   MD4Update(&MD4context, key, keylen);
142   MD4Final(hash, &MD4context);
143 }
144 
145 void
146 HashNtPasswordHash(char *hash, char *hashhash)
147 {
148   MD4_CTX MD4context;
149 
150   MD4Init(&MD4context);
151   MD4Update(&MD4context, hash, 16);
152   MD4Final(hashhash, &MD4context);
153 }
154 
155 static void
156 ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge,
157               char *UserName, char *Challenge)
158 {
159   SHA_CTX Context;
160   char Digest[SHA_DIGEST_LENGTH];
161   char *Name;
162 
163   Name = strrchr(UserName, '\\');
164   if(NULL == Name)
165     Name = UserName;
166   else
167     Name++;
168 
169   SHA1_Init(&Context);
170 
171   SHA1_Update(&Context, PeerChallenge, 16);
172   SHA1_Update(&Context, AuthenticatorChallenge, 16);
173   SHA1_Update(&Context, Name, strlen(Name));
174 
175   SHA1_Final(Digest, &Context);
176   memcpy(Challenge, Digest, 8);
177 }
178 
179 void
180 GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge,
181                    char *UserName, char *Password,
182                    int PasswordLen, char *Response)
183 {
184   char Challenge[8];
185   char PasswordHash[16];
186 
187   ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
188   NtPasswordHash(Password, PasswordLen, PasswordHash);
189   ChallengeResponse(Challenge, PasswordHash, Response);
190 }
191 
192 #define LENGTH 20
193 static char *
194 SHA1_End(SHA_CTX *ctx, char *buf)
195 {
196     int i;
197     unsigned char digest[LENGTH];
198     static const char hex[]="0123456789abcdef";
199 
200     if (!buf)
201         buf = malloc(2*LENGTH + 1);
202     if (!buf)
203         return 0;
204     SHA1_Final(digest, ctx);
205     for (i = 0; i < LENGTH; i++) {
206         buf[i+i] = hex[digest[i] >> 4];
207         buf[i+i+1] = hex[digest[i] & 0x0f];
208     }
209     buf[i+i] = '\0';
210     return buf;
211 }
212 
213 void
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
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
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
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
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
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